[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: [peterbourgon]\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yaml",
    "content": "name: Bug report\ndescription: Report a bug\nlabels: [bug]\nbody:\n- type: textarea\n  attributes:\n    label: What did you do?\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: What did you expect?\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: What happened instead?\n  validations:\n    required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Ask a question\n    url: https://github.com/go-kit/kit/discussions/new?category=q-a\n    about: Questions and discussions with the Go kit community\n\n  - name: Website\n    url: https://gokit.io/\n    about: Project overview, examples, frequently asked questions, etc.\n\n  - name: Reference\n    url: https://pkg.go.dev/github.com/go-kit/kit\n    about: Go kit package documentation\n\n  - name: Slack channel\n    url: https://gophers.slack.com/messages/go-kit\n    about: Real-time discussions and Q&A\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yaml",
    "content": "name: Feature request\ndescription: Suggest new functionality or an enhancement\nbody:\n- type: textarea\n  attributes:\n    label: What would you like?\n  validations:\n    required: true\n"
  },
  {
    "path": ".github/workflows/.editorconfig",
    "content": "[*.yml]\nindent_size = 2\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n\njobs:\n  build:\n    name: Build\n    runs-on: ubuntu-latest\n    strategy:\n      matrix: # Support latest and one minor back\n        go: [\"1.17\", \"1.18\", \"1.19\"]\n    env:\n      GOFLAGS: -mod=readonly\n\n    services:\n      etcd:\n        image: gcr.io/etcd-development/etcd:v3.5.0\n        ports:\n          - 2379\n        env:\n          ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379\n          ETCD_ADVERTISE_CLIENT_URLS: http://0.0.0.0:2379\n        options: --health-cmd \"ETCDCTL_API=3 etcdctl --endpoints http://localhost:2379 endpoint health\" --health-interval 10s --health-timeout 5s --health-retries 5\n\n      consul:\n        image: consul:1.10\n        ports:\n          - 8500\n\n      zk:\n        image: zookeeper:3.5\n        ports:\n          - 2181\n\n      eureka:\n        image: springcloud/eureka\n        ports:\n          - 8761\n        env:\n          eureka.server.responseCacheUpdateIntervalMs: 1000\n\n    steps:\n      - name: Set up Go\n        uses: actions/setup-go@v2.1.3\n        with:\n          stable: \"false\"\n          go-version: ${{ matrix.go }}\n\n      - name: Checkout code\n        uses: actions/checkout@v2\n\n      - name: Run tests\n        env:\n          ETCD_ADDR: http://localhost:${{ job.services.etcd.ports[2379] }}\n          CONSUL_ADDR: localhost:${{ job.services.consul.ports[8500] }}\n          ZK_ADDR: localhost:${{ job.services.zk.ports[2181] }}\n          EUREKA_ADDR: http://localhost:${{ job.services.eureka.ports[8761] }}/eureka\n        run: go test -v -race -coverprofile=coverage.coverprofile -covermode=atomic -tags integration ./...\n\n      - name: Upload coverage\n        uses: codecov/codecov-action@v1\n        with:\n          token: ${{ secrets.CODECOV_TOKEN }}\n          file: coverage.coverprofile\n"
  },
  {
    "path": ".gitignore",
    "content": "*.coverprofile\n\n# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test\n_old*\n\n# Architecture specific extensions/prefixes\n*.[568vq]\n[568vq].out\n\n*.cgo1.go\n*.cgo2.c\n_cgo_defun.c\n_cgo_gotypes.go\n_cgo_export.*\n\n_testmain.go\n\n*.exe\n\n# https://github.com/github/gitignore/blob/master/Global/Vim.gitignore\n# swap\n[._]*.s[a-w][a-z]\n[._]s[a-w][a-z]\n# session\nSession.vim\n# temporary\n.netrwhist\n*~\n# auto-generated tag files\ntags\n\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nFirst, thank you for contributing! We love and encourage pull requests from everyone.\n\nBefore submitting major changes, here are a few guidelines to follow:\n\n1. Check the [open issues][issues] and [pull requests][prs] for existing discussions.\n1. Open an [issue][issues] first, to discuss a new feature or enhancement.\n1. Write tests, and make sure the test suite passes locally and on CI.\n1. Open a pull request, and reference the relevant issue(s).\n1. After receiving feedback, [squash your commits][squash] and add a [great commit message][message].\n1. Have fun!\n\n[issues]: https://github.com/go-kit/kit/issues\n[prs]: https://github.com/go-kit/kit/pulls\n[squash]: http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html\n[message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html\n\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 Peter Bourgon\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "# Go kit\n\n![GitHub Workflow Status](https://github.com/go-kit/kit/workflows/CI/badge.svg)\n[![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)\n[![codecov](https://codecov.io/gh/go-kit/kit/branch/master/graph/badge.svg)](https://codecov.io/gh/go-kit/kit)\n[![Go Report Card](https://goreportcard.com/badge/go-kit/kit)](https://goreportcard.com/report/go-kit/kit)\n[![Sourcegraph](https://sourcegraph.com/github.com/go-kit/kit/-/badge.svg)](https://sourcegraph.com/github.com/go-kit/kit?badge)\n\n**Go kit** is a **programming toolkit** for building microservices\n(or elegant monoliths) in Go. We solve common problems in distributed\nsystems and application architecture so you can focus on delivering\nbusiness value.\n\n- Website: [gokit.io](https://gokit.io)\n- Mailing list: [go-kit](https://groups.google.com/forum/#!forum/go-kit)\n- Slack: [gophers.slack.com](https://gophers.slack.com) **#go-kit** ([invite](https://gophersinvite.herokuapp.com/))\n\n## Sponsors\n\n<div>\n  <a href=\"https://encore.dev\" style=\"display: inline-flex; align-items: center; gap: 10px\">\n    <img src=\"https://user-images.githubusercontent.com/78424526/214602214-52e0483a-b5fc-4d4c-b03e-0b7b23e012df.svg\" height=\"28px\" alt=\"encore icon\"></img>\n  <b>Encore – the platform for building Go-based cloud backends.</b>\n    </a>\n</div>\n<br/>\n\nClick [here](https://github.com/sponsors/peterbourgon) or Sponsor, above, for more information on sponsorship.\n\n## Motivation\n\nGo has emerged as the language of the server, but it remains underrepresented\nin so-called \"modern enterprise\" companies like Facebook, Twitter, Netflix, and\nSoundCloud. Many of these organizations have turned to JVM-based stacks for\ntheir business logic, owing in large part to libraries and ecosystems that\ndirectly support their microservice architectures.\n\nTo reach its next level of success, Go needs more than simple primitives and\nidioms. It needs a comprehensive toolkit, for coherent distributed programming\nin the large. Go kit is a set of packages and best practices, which provide a\ncomprehensive, robust, and trustable way of building microservices for\norganizations of any size.\n\nFor more details, see\n [the website](https://gokit.io),\n [the motivating blog post](http://peter.bourgon.org/go-kit/) and\n [the video of the talk](https://www.youtube.com/watch?v=iFR_7AKkJFU).\nSee also the\n [Go kit talk at GopherCon 2015](https://www.youtube.com/watch?v=1AjaZi4QuGo).\n\n## Goals\n\n- Operate in a heterogeneous SOA — expect to interact with mostly non-Go-kit services\n- RPC as the primary messaging pattern\n- Pluggable serialization and transport — not just JSON over HTTP\n- Operate within existing infrastructures — no mandates for specific tools or technologies\n\n## Non-goals\n\n- Supporting messaging patterns other than RPC (for now) — e.g. MPI, pub/sub, CQRS, etc.\n- Re-implementing functionality that can be provided by adapting existing software\n- Having opinions on operational concerns: deployment, configuration, process supervision, orchestration, etc.\n\n## Contributing\n\nPlease see [CONTRIBUTING.md](/CONTRIBUTING.md).\nThank you, [contributors](https://github.com/go-kit/kit/graphs/contributors)!\n\n## Dependency management\n\nGo kit is [modules](https://github.com/golang/go/wiki/Modules) aware, and we\nencourage users to use the standard modules tooling. But Go kit is at major\nversion 0, so it should be compatible with non-modules environments.\n\n## Code generators\n\nThere are several third-party tools that can generate Go kit code based on\ndifferent starting assumptions.\n\n- [RecoLabs/microgen](https://github.com/RecoLabs/microgen)\n- [GrantZheng/kit](https://github.com/GrantZheng/kit)\n- [kujtimiihoxha/kit](https://github.com/kujtimiihoxha/kit) (unmaintained)\n- [nytimes/marvin](https://github.com/nytimes/marvin)\n- [sagikazarmark/mga](https://github.com/sagikazarmark/mga)\n- [sagikazarmark/protoc-gen-go-kit](https://github.com/sagikazarmark/protoc-gen-go-kit)\n- [metaverse/truss](https://github.com/metaverse/truss)\n- [goadesign/goakit](https://github.com/goadesign/plugins/tree/v3/goakit)\n\n## Related projects\n\nProjects with a ★ have had particular influence on Go kit's design (or vice-versa).\n\n### Service frameworks\n\n- [gizmo](https://github.com/nytimes/gizmo), a microservice toolkit from The New York Times ★\n- [go-micro](https://github.com/micro/go-micro), a distributed systems development framework ★\n- [gotalk](https://github.com/rsms/gotalk), async peer communication protocol &amp; library\n- [Kite](https://github.com/koding/kite), a micro-service framework\n- [gocircuit](https://github.com/gocircuit/circuit), dynamic cloud orchestration\n\n### Individual components\n\n- [afex/hystrix-go](https://github.com/afex/hystrix-go), client-side latency and fault tolerance library\n- [armon/go-metrics](https://github.com/armon/go-metrics), library for exporting performance and runtime metrics to external metrics systems\n- [codahale/lunk](https://github.com/codahale/lunk), structured logging in the style of Google's Dapper or Twitter's Zipkin\n- [eapache/go-resiliency](https://github.com/eapache/go-resiliency), resiliency patterns\n- [sasbury/logging](https://github.com/sasbury/logging), a tagged style of logging\n- [grpc/grpc-go](https://github.com/grpc/grpc-go), HTTP/2 based RPC\n- [inconshreveable/log15](https://github.com/inconshreveable/log15), simple, powerful logging for Go ★\n- [mailgun/vulcand](https://github.com/vulcand/vulcand), programmatic load balancer backed by etcd\n- [mattheath/phosphor](https://github.com/mondough/phosphor), distributed system tracing\n- [pivotal-golang/lager](https://github.com/pivotal-golang/lager), an opinionated logging library\n- [rubyist/circuitbreaker](https://github.com/rubyist/circuitbreaker), circuit breaker library\n- [sirupsen/logrus](https://github.com/sirupsen/logrus), structured, pluggable logging for Go ★\n- [sourcegraph/appdash](https://github.com/sourcegraph/appdash), application tracing system based on Google's Dapper\n- [spacemonkeygo/monitor](https://github.com/spacemonkeygo/monitor), data collection, monitoring, instrumentation, and Zipkin client library\n- [streadway/handy](https://github.com/streadway/handy), net/http handler filters\n- [vitess/rpcplus](https://godoc.org/github.com/youtube/vitess/go/rpcplus), package rpc + context.Context\n- [gdamore/mangos](https://github.com/gdamore/mangos), nanomsg implementation in pure Go\n\n### Web frameworks\n\n- [Gorilla](http://www.gorillatoolkit.org)\n- [Gin](https://gin-gonic.com/)\n- [Negroni](https://github.com/codegangsta/negroni)\n- [Goji](https://github.com/zenazn/goji)\n- [Martini](https://github.com/go-martini/martini)\n- [Beego](https://beego.vip/)\n- [Revel](https://revel.github.io/) (considered [harmful](https://github.com/go-kit/kit/issues/350))\n- [GoBuffalo](https://gobuffalo.io/)\n\n## Additional reading\n\n- [Architecting for the Cloud](https://slideshare.net/stonse/architecting-for-the-cloud-using-netflixoss-codemash-workshop-29852233) — Netflix\n- [Dapper, a Large-Scale Distributed Systems Tracing Infrastructure](http://research.google.com/pubs/pub36356.html) — Google\n- [Your Server as a Function](http://monkey.org/~marius/funsrv.pdf) (PDF) — Twitter\n"
  },
  {
    "path": "auth/basic/README.md",
    "content": "This package provides a Basic Authentication middleware.\n\nIt'll try to compare credentials from Authentication request header to a username/password pair in middleware constructor.\n\nMore details about this type of authentication can be found in [Mozilla article](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).\n\n## Usage\n\n```go\nimport httptransport \"github.com/go-kit/kit/transport/http\"\n\nhttptransport.NewServer(\n\t\tAuthMiddleware(cfg.auth.user, cfg.auth.password, \"Example Realm\")(makeUppercaseEndpoint()),\n\t\tdecodeMappingsRequest,\n\t\thttptransport.EncodeJSONResponse,\n\t\thttptransport.ServerBefore(httptransport.PopulateRequestContext),\n\t)\n```\n\nFor AuthMiddleware to be able to pick up the Authentication header from an HTTP request we need to pass it through the context with something like ```httptransport.ServerBefore(httptransport.PopulateRequestContext)```."
  },
  {
    "path": "auth/basic/middleware.go",
    "content": "package basic\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/sha256\"\n\t\"crypto/subtle\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\thttptransport \"github.com/go-kit/kit/transport/http\"\n)\n\n// AuthError represents an authorization error.\ntype AuthError struct {\n\tRealm string\n}\n\n// StatusCode is an implementation of the StatusCoder interface in go-kit/http.\nfunc (AuthError) StatusCode() int {\n\treturn http.StatusUnauthorized\n}\n\n// Error is an implementation of the Error interface.\nfunc (AuthError) Error() string {\n\treturn http.StatusText(http.StatusUnauthorized)\n}\n\n// Headers is an implementation of the Headerer interface in go-kit/http.\nfunc (e AuthError) Headers() http.Header {\n\treturn http.Header{\n\t\t\"Content-Type\":           []string{\"text/plain; charset=utf-8\"},\n\t\t\"X-Content-Type-Options\": []string{\"nosniff\"},\n\t\t\"WWW-Authenticate\":       []string{fmt.Sprintf(`Basic realm=%q`, e.Realm)},\n\t}\n}\n\n// parseBasicAuth parses an HTTP Basic Authentication string.\n// \"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\" returns ([]byte(\"Aladdin\"), []byte(\"open sesame\"), true).\nfunc parseBasicAuth(auth string) (username, password []byte, ok bool) {\n\tconst prefix = \"Basic \"\n\tif !strings.HasPrefix(auth, prefix) {\n\t\treturn\n\t}\n\tc, err := base64.StdEncoding.DecodeString(auth[len(prefix):])\n\tif err != nil {\n\t\treturn\n\t}\n\n\ts := bytes.IndexByte(c, ':')\n\tif s < 0 {\n\t\treturn\n\t}\n\treturn c[:s], c[s+1:], true\n}\n\n// Returns a hash of a given slice.\nfunc toHashSlice(s []byte) []byte {\n\thash := sha256.Sum256(s)\n\treturn hash[:]\n}\n\n// AuthMiddleware returns a Basic Authentication middleware for a particular user and password.\nfunc AuthMiddleware(requiredUser, requiredPassword, realm string) endpoint.Middleware {\n\trequiredUserBytes := toHashSlice([]byte(requiredUser))\n\trequiredPasswordBytes := toHashSlice([]byte(requiredPassword))\n\n\treturn func(next endpoint.Endpoint) endpoint.Endpoint {\n\t\treturn func(ctx context.Context, request interface{}) (interface{}, error) {\n\t\t\tauth, ok := ctx.Value(httptransport.ContextKeyRequestAuthorization).(string)\n\t\t\tif !ok {\n\t\t\t\treturn nil, AuthError{realm}\n\t\t\t}\n\n\t\t\tgivenUser, givenPassword, ok := parseBasicAuth(auth)\n\t\t\tif !ok {\n\t\t\t\treturn nil, AuthError{realm}\n\t\t\t}\n\n\t\t\tgivenUserBytes := toHashSlice(givenUser)\n\t\t\tgivenPasswordBytes := toHashSlice(givenPassword)\n\n\t\t\tif subtle.ConstantTimeCompare(givenUserBytes, requiredUserBytes) == 0 ||\n\t\t\t\tsubtle.ConstantTimeCompare(givenPasswordBytes, requiredPasswordBytes) == 0 {\n\t\t\t\treturn nil, AuthError{realm}\n\t\t\t}\n\n\t\t\treturn next(ctx, request)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "auth/basic/middleware_test.go",
    "content": "package basic\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"testing\"\n\n\thttptransport \"github.com/go-kit/kit/transport/http\"\n)\n\nfunc TestWithBasicAuth(t *testing.T) {\n\trequiredUser := \"test-user\"\n\trequiredPassword := \"test-pass\"\n\trealm := \"test realm\"\n\n\ttype want struct {\n\t\tresult interface{}\n\t\terr    error\n\t}\n\ttests := []struct {\n\t\tname       string\n\t\tauthHeader interface{}\n\t\twant       want\n\t}{\n\t\t{\"Isn't valid with nil header\", nil, want{nil, AuthError{realm}}},\n\t\t{\"Isn't valid with non-string header\", 42, want{nil, AuthError{realm}}},\n\t\t{\"Isn't valid without authHeader\", \"\", want{nil, AuthError{realm}}},\n\t\t{\"Isn't valid for wrong user\", makeAuthString(\"wrong-user\", requiredPassword), want{nil, AuthError{realm}}},\n\t\t{\"Isn't valid for wrong password\", makeAuthString(requiredUser, \"wrong-password\"), want{nil, AuthError{realm}}},\n\t\t{\"Is valid for correct creds\", makeAuthString(requiredUser, requiredPassword), want{true, nil}},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tctx := context.WithValue(context.TODO(), httptransport.ContextKeyRequestAuthorization, tt.authHeader)\n\n\t\t\tresult, err := AuthMiddleware(requiredUser, requiredPassword, realm)(passedValidation)(ctx, nil)\n\t\t\tif result != tt.want.result || err != tt.want.err {\n\t\t\t\tt.Errorf(\"WithBasicAuth() = result: %v, err: %v, want result: %v, want error: %v\", result, err, tt.want.result, tt.want.err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc makeAuthString(user string, password string) string {\n\tdata := []byte(fmt.Sprintf(\"%s:%s\", user, password))\n\treturn fmt.Sprintf(\"Basic %s\", base64.StdEncoding.EncodeToString(data))\n}\n\nfunc passedValidation(ctx context.Context, request interface{}) (response interface{}, err error) {\n\treturn true, nil\n}\n"
  },
  {
    "path": "auth/casbin/middleware.go",
    "content": "package casbin\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\tstdcasbin \"github.com/casbin/casbin/v2\"\n\t\"github.com/go-kit/kit/endpoint\"\n)\n\ntype contextKey string\n\nconst (\n\t// CasbinModelContextKey holds the key to store the access control model\n\t// in context, it can be a path to configuration file or a casbin/model\n\t// Model.\n\tCasbinModelContextKey contextKey = \"CasbinModel\"\n\n\t// CasbinPolicyContextKey holds the key to store the access control policy\n\t// in context, it can be a path to policy file or an implementation of\n\t// casbin/persist Adapter interface.\n\tCasbinPolicyContextKey contextKey = \"CasbinPolicy\"\n\n\t// CasbinEnforcerContextKey holds the key to retrieve the active casbin\n\t// Enforcer.\n\tCasbinEnforcerContextKey contextKey = \"CasbinEnforcer\"\n)\n\nvar (\n\t// ErrModelContextMissing denotes a casbin model was not passed into\n\t// the parsing of middleware's context.\n\tErrModelContextMissing = errors.New(\"CasbinModel is required in context\")\n\n\t// ErrPolicyContextMissing denotes a casbin policy was not passed into\n\t// the parsing of middleware's context.\n\tErrPolicyContextMissing = errors.New(\"CasbinPolicy is required in context\")\n\n\t// ErrUnauthorized denotes the subject is not authorized to do the action\n\t// intended on the given object, based on the context model and policy.\n\tErrUnauthorized = errors.New(\"Unauthorized Access\")\n)\n\n// NewEnforcer checks whether the subject is authorized to do the specified\n// action on the given object. If a valid access control model and policy\n// is given, then the generated casbin Enforcer is stored in the context\n// with CasbinEnforcer as the key.\nfunc NewEnforcer(\n\tsubject string, object interface{}, action string,\n) endpoint.Middleware {\n\treturn func(next endpoint.Endpoint) endpoint.Endpoint {\n\t\treturn func(ctx context.Context, request interface{}) (response interface{}, err error) {\n\t\t\tcasbinModel := ctx.Value(CasbinModelContextKey)\n\t\t\tcasbinPolicy := ctx.Value(CasbinPolicyContextKey)\n\t\t\tenforcer, err := stdcasbin.NewEnforcer(casbinModel, casbinPolicy)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tctx = context.WithValue(ctx, CasbinEnforcerContextKey, enforcer)\n\t\t\tok, err := enforcer.Enforce(subject, object, action)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif !ok {\n\t\t\t\treturn nil, ErrUnauthorized\n\t\t\t}\n\n\t\t\treturn next(ctx, request)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "auth/casbin/middleware_test.go",
    "content": "package casbin\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\tstdcasbin \"github.com/casbin/casbin/v2\"\n\t\"github.com/casbin/casbin/v2/model\"\n\tfileadapter \"github.com/casbin/casbin/v2/persist/file-adapter\"\n)\n\nfunc TestStructBaseContext(t *testing.T) {\n\te := func(ctx context.Context, i interface{}) (interface{}, error) { return ctx, nil }\n\n\tm := model.NewModel()\n\tm.AddDef(\"r\", \"r\", \"sub, obj, act\")\n\tm.AddDef(\"p\", \"p\", \"sub, obj, act\")\n\tm.AddDef(\"e\", \"e\", \"some(where (p.eft == allow))\")\n\tm.AddDef(\"m\", \"m\", \"r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)\")\n\n\ta := fileadapter.NewAdapter(\"testdata/keymatch_policy.csv\")\n\n\tctx := context.WithValue(context.Background(), CasbinModelContextKey, m)\n\tctx = context.WithValue(ctx, CasbinPolicyContextKey, a)\n\n\t// positive case\n\tmiddleware := NewEnforcer(\"alice\", \"/alice_data/resource1\", \"GET\")(e)\n\tctx1, err := middleware(ctx, struct{}{})\n\tif err != nil {\n\t\tt.Fatalf(\"Enforcer returned error: %s\", err)\n\t}\n\t_, ok := ctx1.(context.Context).Value(CasbinEnforcerContextKey).(*stdcasbin.Enforcer)\n\tif !ok {\n\t\tt.Fatalf(\"context should contains the active enforcer\")\n\t}\n\n\t// negative case\n\tmiddleware = NewEnforcer(\"alice\", \"/alice_data/resource2\", \"POST\")(e)\n\t_, err = middleware(ctx, struct{}{})\n\tif err == nil {\n\t\tt.Fatalf(\"Enforcer should return error\")\n\t}\n}\n\nfunc TestFileBaseContext(t *testing.T) {\n\te := func(ctx context.Context, i interface{}) (interface{}, error) { return ctx, nil }\n\tctx := context.WithValue(context.Background(), CasbinModelContextKey, \"testdata/basic_model.conf\")\n\tctx = context.WithValue(ctx, CasbinPolicyContextKey, \"testdata/basic_policy.csv\")\n\n\t// positive case\n\tmiddleware := NewEnforcer(\"alice\", \"data1\", \"read\")(e)\n\t_, err := middleware(ctx, struct{}{})\n\tif err != nil {\n\t\tt.Fatalf(\"Enforcer returned error: %s\", err)\n\t}\n}\n"
  },
  {
    "path": "auth/casbin/testdata/basic_model.conf",
    "content": "[request_definition]\nr = sub, obj, act\n\n[policy_definition]\np = sub, obj, act\n\n[policy_effect]\ne = some(where (p.eft == allow))\n\n[matchers]\nm = r.sub == p.sub && r.obj == p.obj && r.act == p.act"
  },
  {
    "path": "auth/casbin/testdata/basic_policy.csv",
    "content": "p, alice, data1, read\np, bob, data2, write"
  },
  {
    "path": "auth/casbin/testdata/keymatch_policy.csv",
    "content": "p, alice, /alice_data/*, GET\np, alice, /alice_data/resource1, POST\n\np, bob, /alice_data/resource2, GET\np, bob, /bob_data/*, POST\n\np, cathy, /cathy_data, (GET)|(POST)"
  },
  {
    "path": "auth/jwt/README.md",
    "content": "# package auth/jwt\n\n`package auth/jwt` provides a set of interfaces for service authorization\nthrough [JSON Web Tokens](https://jwt.io/).\n\n## Usage\n\nNewParser takes a key function and an expected signing method and returns an\n`endpoint.Middleware`. The middleware will parse a token passed into the\ncontext via the `jwt.JWTContextKey`. If the token is valid, any claims\nwill be added to the context via the `jwt.JWTClaimsContextKey`.\n\n```go\nimport (\n\tstdjwt \"github.com/golang-jwt/jwt/v4\"\n\n\t\"github.com/go-kit/kit/auth/jwt\"\n\t\"github.com/go-kit/kit/endpoint\"\n)\n\nfunc main() {\n\tvar exampleEndpoint endpoint.Endpoint\n\t{\n\t\tkf := func(token *stdjwt.Token) (interface{}, error) { return []byte(\"SigningString\"), nil }\n\t\texampleEndpoint = MakeExampleEndpoint(service)\n\t\texampleEndpoint = jwt.NewParser(kf, stdjwt.SigningMethodHS256, jwt.StandardClaimsFactory)(exampleEndpoint)\n\t}\n}\n```\n\nNewSigner takes a JWT key ID header, the signing key, signing method, and a\nclaims object. It returns an `endpoint.Middleware`. The middleware will build\nthe token string and add it to the context via the `jwt.JWTContextKey`.\n\n```go\nimport (\n\tstdjwt \"github.com/golang-jwt/jwt/v4\"\n\n\t\"github.com/go-kit/kit/auth/jwt\"\n\t\"github.com/go-kit/kit/endpoint\"\n)\n\nfunc main() {\n\tvar exampleEndpoint endpoint.Endpoint\n\t{\n\t\texampleEndpoint = grpctransport.NewClient(...).Endpoint()\n\t\texampleEndpoint = jwt.NewSigner(\n\t\t\t\"kid-header\",\n\t\t\t[]byte(\"SigningString\"),\n\t\t\tstdjwt.SigningMethodHS256,\n\t\t\tjwt.Claims{},\n\t\t)(exampleEndpoint)\n\t}\n}\n```\n\nIn order for the parser and the signer to work, the authorization headers need\nto be passed between the request and the context. `HTTPToContext()`,\n`ContextToHTTP()`, `GRPCToContext()`, and `ContextToGRPC()` are given as\nhelpers to do this. These functions implement the correlating transport's\nRequestFunc interface and can be passed as ClientBefore or ServerBefore\noptions.\n\nExample of use in a client:\n\n```go\nimport (\n\tstdjwt \"github.com/golang-jwt/jwt/v4\"\n\n\tgrpctransport \"github.com/go-kit/kit/transport/grpc\"\n\t\"github.com/go-kit/kit/auth/jwt\"\n\t\"github.com/go-kit/kit/endpoint\"\n)\n\nfunc main() {\n\n\toptions := []httptransport.ClientOption{}\n\tvar exampleEndpoint endpoint.Endpoint\n\t{\n\t\texampleEndpoint = grpctransport.NewClient(..., grpctransport.ClientBefore(jwt.ContextToGRPC())).Endpoint()\n\t\texampleEndpoint = jwt.NewSigner(\n\t\t\t\"kid-header\",\n\t\t\t[]byte(\"SigningString\"),\n\t\t\tstdjwt.SigningMethodHS256,\n\t\t\tjwt.Claims{},\n\t\t)(exampleEndpoint)\n\t}\n}\n```\n\nExample of use in a server:\n\n```go\nimport (\n\t\"context\"\n\n\t\"github.com/go-kit/kit/auth/jwt\"\n\t\"github.com/go-kit/log\"\n\tgrpctransport \"github.com/go-kit/kit/transport/grpc\"\n)\n\nfunc MakeGRPCServer(ctx context.Context, endpoints Endpoints, logger log.Logger) pb.ExampleServer {\n\toptions := []grpctransport.ServerOption{grpctransport.ServerErrorLogger(logger)}\n\n\treturn &grpcServer{\n\t\tcreateUser: grpctransport.NewServer(\n\t\t\tctx,\n\t\t\tendpoints.CreateUserEndpoint,\n\t\t\tDecodeGRPCCreateUserRequest,\n\t\t\tEncodeGRPCCreateUserResponse,\n\t\t\tappend(options, grpctransport.ServerBefore(jwt.GRPCToContext()))...,\n\t\t),\n\t\tgetUser: grpctransport.NewServer(\n\t\t\tctx,\n\t\t\tendpoints.GetUserEndpoint,\n\t\t\tDecodeGRPCGetUserRequest,\n\t\t\tEncodeGRPCGetUserResponse,\n\t\t\toptions...,\n\t\t),\n\t}\n}\n```\n"
  },
  {
    "path": "auth/jwt/middleware.go",
    "content": "package jwt\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/golang-jwt/jwt/v4\"\n)\n\ntype contextKey string\n\nconst (\n\t// JWTContextKey holds the key used to store a JWT in the context.\n\tJWTContextKey contextKey = \"JWTToken\"\n\n\t// JWTTokenContextKey is an alias for JWTContextKey.\n\t//\n\t// Deprecated: prefer JWTContextKey.\n\tJWTTokenContextKey = JWTContextKey\n\n\t// JWTClaimsContextKey holds the key used to store the JWT Claims in the\n\t// context.\n\tJWTClaimsContextKey contextKey = \"JWTClaims\"\n)\n\nvar (\n\t// ErrTokenContextMissing denotes a token was not passed into the parsing\n\t// middleware's context.\n\tErrTokenContextMissing = errors.New(\"token up for parsing was not passed through the context\")\n\n\t// ErrTokenInvalid denotes a token was not able to be validated.\n\tErrTokenInvalid = errors.New(\"JWT was invalid\")\n\n\t// ErrTokenExpired denotes a token's expire header (exp) has since passed.\n\tErrTokenExpired = errors.New(\"JWT is expired\")\n\n\t// ErrTokenMalformed denotes a token was not formatted as a JWT.\n\tErrTokenMalformed = errors.New(\"JWT is malformed\")\n\n\t// ErrTokenNotActive denotes a token's not before header (nbf) is in the\n\t// future.\n\tErrTokenNotActive = errors.New(\"token is not valid yet\")\n\n\t// ErrUnexpectedSigningMethod denotes a token was signed with an unexpected\n\t// signing method.\n\tErrUnexpectedSigningMethod = errors.New(\"unexpected signing method\")\n)\n\n// NewSigner creates a new JWT generating middleware, specifying key ID,\n// signing string, signing method and the claims you would like it to contain.\n// Tokens are signed with a Key ID header (kid) which is useful for determining\n// the key to use for parsing. Particularly useful for clients.\nfunc NewSigner(kid string, key []byte, method jwt.SigningMethod, claims jwt.Claims) endpoint.Middleware {\n\treturn func(next endpoint.Endpoint) endpoint.Endpoint {\n\t\treturn func(ctx context.Context, request interface{}) (response interface{}, err error) {\n\t\t\ttoken := jwt.NewWithClaims(method, claims)\n\t\t\ttoken.Header[\"kid\"] = kid\n\n\t\t\t// Sign and get the complete encoded token as a string using the secret\n\t\t\ttokenString, err := token.SignedString(key)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tctx = context.WithValue(ctx, JWTContextKey, tokenString)\n\n\t\t\treturn next(ctx, request)\n\t\t}\n\t}\n}\n\n// ClaimsFactory is a factory for jwt.Claims.\n// Useful in NewParser middleware.\ntype ClaimsFactory func() jwt.Claims\n\n// MapClaimsFactory is a ClaimsFactory that returns\n// an empty jwt.MapClaims.\nfunc MapClaimsFactory() jwt.Claims {\n\treturn jwt.MapClaims{}\n}\n\n// StandardClaimsFactory is a ClaimsFactory that returns\n// an empty jwt.StandardClaims.\nfunc StandardClaimsFactory() jwt.Claims {\n\treturn &jwt.StandardClaims{}\n}\n\n// NewParser creates a new JWT parsing middleware, specifying a\n// jwt.Keyfunc interface, the signing method and the claims type to be used. NewParser\n// adds the resulting claims to endpoint context or returns error on invalid token.\n// Particularly useful for servers.\nfunc NewParser(keyFunc jwt.Keyfunc, method jwt.SigningMethod, newClaims ClaimsFactory) endpoint.Middleware {\n\treturn func(next endpoint.Endpoint) endpoint.Endpoint {\n\t\treturn func(ctx context.Context, request interface{}) (response interface{}, err error) {\n\t\t\t// tokenString is stored in the context from the transport handlers.\n\t\t\ttokenString, ok := ctx.Value(JWTContextKey).(string)\n\t\t\tif !ok {\n\t\t\t\treturn nil, ErrTokenContextMissing\n\t\t\t}\n\n\t\t\t// Parse takes the token string and a function for looking up the\n\t\t\t// key. The latter is especially useful if you use multiple keys\n\t\t\t// for your application.  The standard is to use 'kid' in the head\n\t\t\t// of the token to identify which key to use, but the parsed token\n\t\t\t// (head and claims) is provided to the callback, providing\n\t\t\t// flexibility.\n\t\t\ttoken, err := jwt.ParseWithClaims(tokenString, newClaims(), func(token *jwt.Token) (interface{}, error) {\n\t\t\t\t// Don't forget to validate the alg is what you expect:\n\t\t\t\tif token.Method != method {\n\t\t\t\t\treturn nil, ErrUnexpectedSigningMethod\n\t\t\t\t}\n\n\t\t\t\treturn keyFunc(token)\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\tif e, ok := err.(*jwt.ValidationError); ok {\n\t\t\t\t\tswitch {\n\t\t\t\t\tcase e.Errors&jwt.ValidationErrorMalformed != 0:\n\t\t\t\t\t\t// Token is malformed\n\t\t\t\t\t\treturn nil, ErrTokenMalformed\n\t\t\t\t\tcase e.Errors&jwt.ValidationErrorExpired != 0:\n\t\t\t\t\t\t// Token is expired\n\t\t\t\t\t\treturn nil, ErrTokenExpired\n\t\t\t\t\tcase e.Errors&jwt.ValidationErrorNotValidYet != 0:\n\t\t\t\t\t\t// Token is not active yet\n\t\t\t\t\t\treturn nil, ErrTokenNotActive\n\t\t\t\t\tcase e.Inner != nil:\n\t\t\t\t\t\t// report e.Inner\n\t\t\t\t\t\treturn nil, e.Inner\n\t\t\t\t\t}\n\t\t\t\t\t// We have a ValidationError but have no specific Go kit error for it.\n\t\t\t\t\t// Fall through to return original error.\n\t\t\t\t}\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tif !token.Valid {\n\t\t\t\treturn nil, ErrTokenInvalid\n\t\t\t}\n\n\t\t\tctx = context.WithValue(ctx, JWTClaimsContextKey, token.Claims)\n\n\t\t\treturn next(ctx, request)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "auth/jwt/middleware_test.go",
    "content": "package jwt\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"crypto/subtle\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/golang-jwt/jwt/v4\"\n)\n\ntype customClaims struct {\n\tMyProperty string `json:\"my_property\"`\n\tjwt.StandardClaims\n}\n\nfunc (c customClaims) VerifyMyProperty(p string) bool {\n\treturn subtle.ConstantTimeCompare([]byte(c.MyProperty), []byte(p)) != 0\n}\n\nvar (\n\tkid            = \"kid\"\n\tkey            = []byte(\"test_signing_key\")\n\tmyProperty     = \"some value\"\n\tmethod         = jwt.SigningMethodHS256\n\tinvalidMethod  = jwt.SigningMethodRS256\n\tmapClaims      = jwt.MapClaims{\"user\": \"go-kit\"}\n\tstandardClaims = jwt.StandardClaims{Audience: \"go-kit\"}\n\tmyCustomClaims = customClaims{MyProperty: myProperty, StandardClaims: standardClaims}\n\t// Signed tokens generated at https://jwt.io/\n\tsignedKey         = \"eyJhbGciOiJIUzI1NiIsImtpZCI6ImtpZCIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiZ28ta2l0In0.14M2VmYyApdSlV_LZ88ajjwuaLeIFplB8JpyNy0A19E\"\n\tstandardSignedKey = \"eyJhbGciOiJIUzI1NiIsImtpZCI6ImtpZCIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJnby1raXQifQ.L5ypIJjCOOv3jJ8G5SelaHvR04UJuxmcBN5QW3m_aoY\"\n\tcustomSignedKey   = \"eyJhbGciOiJIUzI1NiIsImtpZCI6ImtpZCIsInR5cCI6IkpXVCJ9.eyJteV9wcm9wZXJ0eSI6InNvbWUgdmFsdWUiLCJhdWQiOiJnby1raXQifQ.s8F-IDrV4WPJUsqr7qfDi-3GRlcKR0SRnkTeUT_U-i0\"\n\tinvalidKey        = \"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.e30.vKVCKto-Wn6rgz3vBdaZaCBGfCBDTXOENSo_X2Gq7qA\"\n\tmalformedKey      = \"malformed.jwt.token\"\n)\n\nfunc signingValidator(t *testing.T, signer endpoint.Endpoint, expectedKey string) {\n\tctx, err := signer(context.Background(), struct{}{})\n\tif err != nil {\n\t\tt.Fatalf(\"Signer returned error: %s\", err)\n\t}\n\n\ttoken, ok := ctx.(context.Context).Value(JWTContextKey).(string)\n\tif !ok {\n\t\tt.Fatal(\"Token did not exist in context\")\n\t}\n\n\tif token != expectedKey {\n\t\tt.Fatalf(\"JWTs did not match: expecting %s got %s\", expectedKey, token)\n\t}\n}\n\nfunc TestNewSigner(t *testing.T) {\n\te := func(ctx context.Context, i interface{}) (interface{}, error) { return ctx, nil }\n\n\tsigner := NewSigner(kid, key, method, mapClaims)(e)\n\tsigningValidator(t, signer, signedKey)\n\n\tsigner = NewSigner(kid, key, method, standardClaims)(e)\n\tsigningValidator(t, signer, standardSignedKey)\n\n\tsigner = NewSigner(kid, key, method, myCustomClaims)(e)\n\tsigningValidator(t, signer, customSignedKey)\n}\n\nfunc TestJWTParser(t *testing.T) {\n\te := func(ctx context.Context, i interface{}) (interface{}, error) { return ctx, nil }\n\n\tkeys := func(token *jwt.Token) (interface{}, error) {\n\t\treturn key, nil\n\t}\n\n\tparser := NewParser(keys, method, MapClaimsFactory)(e)\n\n\t// No Token is passed into the parser\n\t_, err := parser(context.Background(), struct{}{})\n\tif err == nil {\n\t\tt.Error(\"Parser should have returned an error\")\n\t}\n\n\tif err != ErrTokenContextMissing {\n\t\tt.Errorf(\"unexpected error returned, expected: %s got: %s\", ErrTokenContextMissing, err)\n\t}\n\n\t// Invalid Token is passed into the parser\n\tctx := context.WithValue(context.Background(), JWTContextKey, invalidKey)\n\t_, err = parser(ctx, struct{}{})\n\tif err == nil {\n\t\tt.Error(\"Parser should have returned an error\")\n\t}\n\n\t// Invalid Method is used in the parser\n\tbadParser := NewParser(keys, invalidMethod, MapClaimsFactory)(e)\n\tctx = context.WithValue(context.Background(), JWTContextKey, signedKey)\n\t_, err = badParser(ctx, struct{}{})\n\tif err == nil {\n\t\tt.Error(\"Parser should have returned an error\")\n\t}\n\n\tif err != ErrUnexpectedSigningMethod {\n\t\tt.Errorf(\"unexpected error returned, expected: %s got: %s\", ErrUnexpectedSigningMethod, err)\n\t}\n\n\t// Invalid key is used in the parser\n\tinvalidKeys := func(token *jwt.Token) (interface{}, error) {\n\t\treturn []byte(\"bad\"), nil\n\t}\n\n\tbadParser = NewParser(invalidKeys, method, MapClaimsFactory)(e)\n\tctx = context.WithValue(context.Background(), JWTContextKey, signedKey)\n\t_, err = badParser(ctx, struct{}{})\n\tif err == nil {\n\t\tt.Error(\"Parser should have returned an error\")\n\t}\n\n\t// Correct token is passed into the parser\n\tctx = context.WithValue(context.Background(), JWTContextKey, signedKey)\n\tctx1, err := parser(ctx, struct{}{})\n\tif err != nil {\n\t\tt.Fatalf(\"Parser returned error: %s\", err)\n\t}\n\n\tcl, ok := ctx1.(context.Context).Value(JWTClaimsContextKey).(jwt.MapClaims)\n\tif !ok {\n\t\tt.Fatal(\"Claims were not passed into context correctly\")\n\t}\n\n\tif cl[\"user\"] != mapClaims[\"user\"] {\n\t\tt.Fatalf(\"JWT Claims.user did not match: expecting %s got %s\", mapClaims[\"user\"], cl[\"user\"])\n\t}\n\n\t// Test for malformed token error response\n\tparser = NewParser(keys, method, StandardClaimsFactory)(e)\n\tctx = context.WithValue(context.Background(), JWTContextKey, malformedKey)\n\tctx1, err = parser(ctx, struct{}{})\n\tif want, have := ErrTokenMalformed, err; want != have {\n\t\tt.Fatalf(\"Expected %+v, got %+v\", want, have)\n\t}\n\n\t// Test for expired token error response\n\tparser = NewParser(keys, method, StandardClaimsFactory)(e)\n\texpired := jwt.NewWithClaims(method, jwt.StandardClaims{ExpiresAt: time.Now().Unix() - 100})\n\ttoken, err := expired.SignedString(key)\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to Sign Token: %+v\", err)\n\t}\n\tctx = context.WithValue(context.Background(), JWTContextKey, token)\n\tctx1, err = parser(ctx, struct{}{})\n\tif want, have := ErrTokenExpired, err; want != have {\n\t\tt.Fatalf(\"Expected %+v, got %+v\", want, have)\n\t}\n\n\t// Test for not activated token error response\n\tparser = NewParser(keys, method, StandardClaimsFactory)(e)\n\tnotactive := jwt.NewWithClaims(method, jwt.StandardClaims{NotBefore: time.Now().Unix() + 100})\n\ttoken, err = notactive.SignedString(key)\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to Sign Token: %+v\", err)\n\t}\n\tctx = context.WithValue(context.Background(), JWTContextKey, token)\n\tctx1, err = parser(ctx, struct{}{})\n\tif want, have := ErrTokenNotActive, err; want != have {\n\t\tt.Fatalf(\"Expected %+v, got %+v\", want, have)\n\t}\n\n\t// test valid standard claims token\n\tparser = NewParser(keys, method, StandardClaimsFactory)(e)\n\tctx = context.WithValue(context.Background(), JWTContextKey, standardSignedKey)\n\tctx1, err = parser(ctx, struct{}{})\n\tif err != nil {\n\t\tt.Fatalf(\"Parser returned error: %s\", err)\n\t}\n\tstdCl, ok := ctx1.(context.Context).Value(JWTClaimsContextKey).(*jwt.StandardClaims)\n\tif !ok {\n\t\tt.Fatal(\"Claims were not passed into context correctly\")\n\t}\n\tif !stdCl.VerifyAudience(\"go-kit\", true) {\n\t\tt.Fatalf(\"JWT jwt.StandardClaims.Audience did not match: expecting %s got %s\", standardClaims.Audience, stdCl.Audience)\n\t}\n\n\t// test valid customized claims token\n\tparser = NewParser(keys, method, func() jwt.Claims { return &customClaims{} })(e)\n\tctx = context.WithValue(context.Background(), JWTContextKey, customSignedKey)\n\tctx1, err = parser(ctx, struct{}{})\n\tif err != nil {\n\t\tt.Fatalf(\"Parser returned error: %s\", err)\n\t}\n\tcustCl, ok := ctx1.(context.Context).Value(JWTClaimsContextKey).(*customClaims)\n\tif !ok {\n\t\tt.Fatal(\"Claims were not passed into context correctly\")\n\t}\n\tif !custCl.VerifyAudience(\"go-kit\", true) {\n\t\tt.Fatalf(\"JWT customClaims.Audience did not match: expecting %s got %s\", standardClaims.Audience, custCl.Audience)\n\t}\n\tif !custCl.VerifyMyProperty(myProperty) {\n\t\tt.Fatalf(\"JWT customClaims.MyProperty did not match: expecting %s got %s\", myProperty, custCl.MyProperty)\n\t}\n}\n\nfunc TestIssue562(t *testing.T) {\n\tvar (\n\t\tkf  = func(token *jwt.Token) (interface{}, error) { return []byte(\"secret\"), nil }\n\t\te   = NewParser(kf, jwt.SigningMethodHS256, MapClaimsFactory)(endpoint.Nop)\n\t\tkey = JWTContextKey\n\t\tval = \"eyJhbGciOiJIUzI1NiIsImtpZCI6ImtpZCIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiZ28ta2l0In0.14M2VmYyApdSlV_LZ88ajjwuaLeIFplB8JpyNy0A19E\"\n\t\tctx = context.WithValue(context.Background(), key, val)\n\t)\n\twg := sync.WaitGroup{}\n\tfor i := 0; i < 100; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\te(ctx, struct{}{}) // fatal error: concurrent map read and map write\n\t\t}()\n\t}\n\twg.Wait()\n}\n"
  },
  {
    "path": "auth/jwt/transport.go",
    "content": "package jwt\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\tstdhttp \"net/http\"\n\t\"strings\"\n\n\t\"google.golang.org/grpc/metadata\"\n\n\t\"github.com/go-kit/kit/transport/grpc\"\n\t\"github.com/go-kit/kit/transport/http\"\n)\n\nconst (\n\tbearer       string = \"bearer\"\n\tbearerFormat string = \"Bearer %s\"\n)\n\n// HTTPToContext moves a JWT from request header to context. Particularly\n// useful for servers.\nfunc HTTPToContext() http.RequestFunc {\n\treturn func(ctx context.Context, r *stdhttp.Request) context.Context {\n\t\ttoken, ok := extractTokenFromAuthHeader(r.Header.Get(\"Authorization\"))\n\t\tif !ok {\n\t\t\treturn ctx\n\t\t}\n\n\t\treturn context.WithValue(ctx, JWTContextKey, token)\n\t}\n}\n\n// ContextToHTTP moves a JWT from context to request header. Particularly\n// useful for clients.\nfunc ContextToHTTP() http.RequestFunc {\n\treturn func(ctx context.Context, r *stdhttp.Request) context.Context {\n\t\ttoken, ok := ctx.Value(JWTContextKey).(string)\n\t\tif ok {\n\t\t\tr.Header.Add(\"Authorization\", generateAuthHeaderFromToken(token))\n\t\t}\n\t\treturn ctx\n\t}\n}\n\n// GRPCToContext moves a JWT from grpc metadata to context. Particularly\n// userful for servers.\nfunc GRPCToContext() grpc.ServerRequestFunc {\n\treturn func(ctx context.Context, md metadata.MD) context.Context {\n\t\t// capital \"Key\" is illegal in HTTP/2.\n\t\tauthHeader, ok := md[\"authorization\"]\n\t\tif !ok {\n\t\t\treturn ctx\n\t\t}\n\n\t\ttoken, ok := extractTokenFromAuthHeader(authHeader[0])\n\t\tif ok {\n\t\t\tctx = context.WithValue(ctx, JWTContextKey, token)\n\t\t}\n\n\t\treturn ctx\n\t}\n}\n\n// ContextToGRPC moves a JWT from context to grpc metadata. Particularly\n// useful for clients.\nfunc ContextToGRPC() grpc.ClientRequestFunc {\n\treturn func(ctx context.Context, md *metadata.MD) context.Context {\n\t\ttoken, ok := ctx.Value(JWTContextKey).(string)\n\t\tif ok {\n\t\t\t// capital \"Key\" is illegal in HTTP/2.\n\t\t\t(*md)[\"authorization\"] = []string{generateAuthHeaderFromToken(token)}\n\t\t}\n\n\t\treturn ctx\n\t}\n}\n\nfunc extractTokenFromAuthHeader(val string) (token string, ok bool) {\n\tauthHeaderParts := strings.Split(val, \" \")\n\tif len(authHeaderParts) != 2 || !strings.EqualFold(authHeaderParts[0], bearer) {\n\t\treturn \"\", false\n\t}\n\n\treturn authHeaderParts[1], true\n}\n\nfunc generateAuthHeaderFromToken(token string) string {\n\treturn fmt.Sprintf(bearerFormat, token)\n}\n"
  },
  {
    "path": "auth/jwt/transport_test.go",
    "content": "package jwt\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"google.golang.org/grpc/metadata\"\n)\n\nfunc TestHTTPToContext(t *testing.T) {\n\treqFunc := HTTPToContext()\n\n\t// When the header doesn't exist\n\tctx := reqFunc(context.Background(), &http.Request{})\n\n\tif ctx.Value(JWTContextKey) != nil {\n\t\tt.Error(\"Context shouldn't contain the encoded JWT\")\n\t}\n\n\t// Authorization header value has invalid format\n\theader := http.Header{}\n\theader.Set(\"Authorization\", \"no expected auth header format value\")\n\tctx = reqFunc(context.Background(), &http.Request{Header: header})\n\n\tif ctx.Value(JWTContextKey) != nil {\n\t\tt.Error(\"Context shouldn't contain the encoded JWT\")\n\t}\n\n\t// Authorization header is correct\n\theader.Set(\"Authorization\", generateAuthHeaderFromToken(signedKey))\n\tctx = reqFunc(context.Background(), &http.Request{Header: header})\n\n\ttoken := ctx.Value(JWTContextKey).(string)\n\tif token != signedKey {\n\t\tt.Errorf(\"Context doesn't contain the expected encoded token value; expected: %s, got: %s\", signedKey, token)\n\t}\n}\n\nfunc TestContextToHTTP(t *testing.T) {\n\treqFunc := ContextToHTTP()\n\n\t// No JWT is passed in the context\n\tctx := context.Background()\n\tr := http.Request{}\n\treqFunc(ctx, &r)\n\n\ttoken := r.Header.Get(\"Authorization\")\n\tif token != \"\" {\n\t\tt.Error(\"authorization key should not exist in metadata\")\n\t}\n\n\t// Correct JWT is passed in the context\n\tctx = context.WithValue(context.Background(), JWTContextKey, signedKey)\n\tr = http.Request{Header: http.Header{}}\n\treqFunc(ctx, &r)\n\n\ttoken = r.Header.Get(\"Authorization\")\n\texpected := generateAuthHeaderFromToken(signedKey)\n\n\tif token != expected {\n\t\tt.Errorf(\"Authorization header does not contain the expected JWT; expected %s, got %s\", expected, token)\n\t}\n}\n\nfunc TestGRPCToContext(t *testing.T) {\n\tmd := metadata.MD{}\n\treqFunc := GRPCToContext()\n\n\t// No Authorization header is passed\n\tctx := reqFunc(context.Background(), md)\n\ttoken := ctx.Value(JWTContextKey)\n\tif token != nil {\n\t\tt.Error(\"Context should not contain a JWT\")\n\t}\n\n\t// Invalid Authorization header is passed\n\tmd[\"authorization\"] = []string{signedKey}\n\tctx = reqFunc(context.Background(), md)\n\ttoken = ctx.Value(JWTContextKey)\n\tif token != nil {\n\t\tt.Error(\"Context should not contain a JWT\")\n\t}\n\n\t// Authorization header is correct\n\tmd[\"authorization\"] = []string{fmt.Sprintf(\"Bearer %s\", signedKey)}\n\tctx = reqFunc(context.Background(), md)\n\ttoken, ok := ctx.Value(JWTContextKey).(string)\n\tif !ok {\n\t\tt.Fatal(\"JWT not passed to context correctly\")\n\t}\n\n\tif token != signedKey {\n\t\tt.Errorf(\"JWTs did not match: expecting %s got %s\", signedKey, token)\n\t}\n}\n\nfunc TestContextToGRPC(t *testing.T) {\n\treqFunc := ContextToGRPC()\n\n\t// No JWT is passed in the context\n\tctx := context.Background()\n\tmd := metadata.MD{}\n\treqFunc(ctx, &md)\n\n\t_, ok := md[\"authorization\"]\n\tif ok {\n\t\tt.Error(\"authorization key should not exist in metadata\")\n\t}\n\n\t// Correct JWT is passed in the context\n\tctx = context.WithValue(context.Background(), JWTContextKey, signedKey)\n\tmd = metadata.MD{}\n\treqFunc(ctx, &md)\n\n\ttoken, ok := md[\"authorization\"]\n\tif !ok {\n\t\tt.Fatal(\"JWT not passed to metadata correctly\")\n\t}\n\n\tif token[0] != generateAuthHeaderFromToken(signedKey) {\n\t\tt.Errorf(\"JWTs did not match: expecting %s got %s\", signedKey, token[0])\n\t}\n}\n"
  },
  {
    "path": "circuitbreaker/doc.go",
    "content": "// Package circuitbreaker implements the circuit breaker pattern.\n//\n// Circuit breakers prevent thundering herds, and improve resiliency against\n// intermittent errors. Every client-side endpoint should be wrapped in a\n// circuit breaker.\n//\n// We provide several implementations in this package, but if you're looking\n// for guidance, Gobreaker is probably the best place to start.  It has a\n// simple and intuitive API, and is well-tested.\npackage circuitbreaker\n"
  },
  {
    "path": "circuitbreaker/gobreaker.go",
    "content": "package circuitbreaker\n\nimport (\n\t\"context\"\n\n\t\"github.com/sony/gobreaker\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n)\n\n// Gobreaker returns an endpoint.Middleware that implements the circuit\n// breaker pattern using the sony/gobreaker package. Only errors returned by\n// the wrapped endpoint count against the circuit breaker's error count.\n//\n// See http://godoc.org/github.com/sony/gobreaker for more information.\nfunc Gobreaker(cb *gobreaker.CircuitBreaker) endpoint.Middleware {\n\treturn func(next endpoint.Endpoint) endpoint.Endpoint {\n\t\treturn func(ctx context.Context, request interface{}) (interface{}, error) {\n\t\t\treturn cb.Execute(func() (interface{}, error) { return next(ctx, request) })\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "circuitbreaker/gobreaker_test.go",
    "content": "package circuitbreaker_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/sony/gobreaker\"\n\n\t\"github.com/go-kit/kit/circuitbreaker\"\n)\n\nfunc TestGobreaker(t *testing.T) {\n\tvar (\n\t\tbreaker          = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{}))\n\t\tprimeWith        = 100\n\t\tshouldPass       = func(n int) bool { return n <= 5 } // https://github.com/sony/gobreaker/blob/bfa846d/gobreaker.go#L76\n\t\tcircuitOpenError = \"circuit breaker is open\"\n\t)\n\ttestFailingEndpoint(t, breaker, primeWith, shouldPass, 0, circuitOpenError)\n}\n"
  },
  {
    "path": "circuitbreaker/handy_breaker.go",
    "content": "package circuitbreaker\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/streadway/handy/breaker\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n)\n\n// HandyBreaker returns an endpoint.Middleware that implements the circuit\n// breaker pattern using the streadway/handy/breaker package. Only errors\n// returned by the wrapped endpoint count against the circuit breaker's error\n// count.\n//\n// See http://godoc.org/github.com/streadway/handy/breaker for more\n// information.\nfunc HandyBreaker(cb breaker.Breaker) endpoint.Middleware {\n\treturn func(next endpoint.Endpoint) endpoint.Endpoint {\n\t\treturn func(ctx context.Context, request interface{}) (response interface{}, err error) {\n\t\t\tif !cb.Allow() {\n\t\t\t\treturn nil, breaker.ErrCircuitOpen\n\t\t\t}\n\n\t\t\tdefer func(begin time.Time) {\n\t\t\t\tif err == nil {\n\t\t\t\t\tcb.Success(time.Since(begin))\n\t\t\t\t} else {\n\t\t\t\t\tcb.Failure(time.Since(begin))\n\t\t\t\t}\n\t\t\t}(time.Now())\n\n\t\t\tresponse, err = next(ctx, request)\n\t\t\treturn\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "circuitbreaker/handy_breaker_test.go",
    "content": "package circuitbreaker_test\n\nimport (\n\t\"testing\"\n\n\thandybreaker \"github.com/streadway/handy/breaker\"\n\n\t\"github.com/go-kit/kit/circuitbreaker\"\n)\n\nfunc TestHandyBreaker(t *testing.T) {\n\tvar (\n\t\tfailureRatio     = 0.05\n\t\tbreaker          = circuitbreaker.HandyBreaker(handybreaker.NewBreaker(failureRatio))\n\t\tprimeWith        = handybreaker.DefaultMinObservations * 10\n\t\tshouldPass       = func(n int) bool { return (float64(n) / float64(primeWith+n)) <= failureRatio }\n\t\topenCircuitError = handybreaker.ErrCircuitOpen.Error()\n\t)\n\ttestFailingEndpoint(t, breaker, primeWith, shouldPass, 0, openCircuitError)\n}\n"
  },
  {
    "path": "circuitbreaker/hystrix.go",
    "content": "package circuitbreaker\n\nimport (\n\t\"context\"\n\n\t\"github.com/afex/hystrix-go/hystrix\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n)\n\n// Hystrix returns an endpoint.Middleware that implements the circuit\n// breaker pattern using the afex/hystrix-go package.\n//\n// When using this circuit breaker, please configure your commands separately.\n//\n// See https://godoc.org/github.com/afex/hystrix-go/hystrix for more\n// information.\nfunc Hystrix(commandName string) endpoint.Middleware {\n\treturn func(next endpoint.Endpoint) endpoint.Endpoint {\n\t\treturn func(ctx context.Context, request interface{}) (response interface{}, err error) {\n\t\t\tvar resp interface{}\n\t\t\tif err := hystrix.Do(commandName, func() (err error) {\n\t\t\t\tresp, err = next(ctx, request)\n\t\t\t\treturn err\n\t\t\t}, nil); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn resp, nil\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "circuitbreaker/hystrix_test.go",
    "content": "package circuitbreaker_test\n\nimport (\n\t\"io/ioutil\"\n\tstdlog \"log\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/afex/hystrix-go/hystrix\"\n\n\t\"github.com/go-kit/kit/circuitbreaker\"\n)\n\nfunc TestHystrix(t *testing.T) {\n\tstdlog.SetOutput(ioutil.Discard)\n\n\tconst (\n\t\tcommandName   = \"my-endpoint\"\n\t\terrorPercent  = 5\n\t\tmaxConcurrent = 1000\n\t)\n\thystrix.ConfigureCommand(commandName, hystrix.CommandConfig{\n\t\tErrorPercentThreshold: errorPercent,\n\t\tMaxConcurrentRequests: maxConcurrent,\n\t})\n\n\tvar (\n\t\tbreaker          = circuitbreaker.Hystrix(commandName)\n\t\tprimeWith        = hystrix.DefaultVolumeThreshold * 2\n\t\tshouldPass       = func(n int) bool { return (float64(n) / float64(primeWith+n)) <= (float64(errorPercent-1) / 100.0) }\n\t\topenCircuitError = hystrix.ErrCircuitOpen.Error()\n\t)\n\n\t// hystrix-go uses buffered channels to receive reports on request success/failure,\n\t// and so is basically impossible to test deterministically. We have to make sure\n\t// the report buffer is emptied, by injecting a sleep between each invocation.\n\trequestDelay := 5 * time.Millisecond\n\n\ttestFailingEndpoint(t, breaker, primeWith, shouldPass, requestDelay, openCircuitError)\n}\n"
  },
  {
    "path": "circuitbreaker/util_test.go",
    "content": "package circuitbreaker_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n)\n\nfunc testFailingEndpoint(\n\tt *testing.T,\n\tbreaker endpoint.Middleware,\n\tprimeWith int,\n\tshouldPass func(int) bool,\n\trequestDelay time.Duration,\n\topenCircuitError string,\n) {\n\t_, file, line, _ := runtime.Caller(1)\n\tcaller := fmt.Sprintf(\"%s:%d\", filepath.Base(file), line)\n\n\t// Create a mock endpoint and wrap it with the breaker.\n\tm := mock{}\n\tvar e endpoint.Endpoint\n\te = m.endpoint\n\te = breaker(e)\n\n\t// Prime the endpoint with successful requests.\n\tfor i := 0; i < primeWith; i++ {\n\t\tif _, err := e(context.Background(), struct{}{}); err != nil {\n\t\t\tt.Fatalf(\"%s: during priming, got error: %v\", caller, err)\n\t\t}\n\t\ttime.Sleep(requestDelay)\n\t}\n\n\t// Switch the endpoint to start throwing errors.\n\tm.err = errors.New(\"tragedy+disaster\")\n\tm.through = 0\n\n\t// The first several should be allowed through and yield our error.\n\tfor i := 0; shouldPass(i); i++ {\n\t\tif _, err := e(context.Background(), struct{}{}); err != m.err {\n\t\t\tt.Fatalf(\"%s: want %v, have %v\", caller, m.err, err)\n\t\t}\n\t\ttime.Sleep(requestDelay)\n\t}\n\tthrough := m.through\n\n\t// But the rest should be blocked by an open circuit.\n\tfor i := 0; i < 10; i++ {\n\t\tif _, err := e(context.Background(), struct{}{}); err.Error() != openCircuitError {\n\t\t\tt.Fatalf(\"%s: want %q, have %q\", caller, openCircuitError, err.Error())\n\t\t}\n\t\ttime.Sleep(requestDelay)\n\t}\n\n\t// Make sure none of those got through.\n\tif want, have := through, m.through; want != have {\n\t\tt.Errorf(\"%s: want %d, have %d\", caller, want, have)\n\t}\n}\n\ntype mock struct {\n\tthrough int\n\terr     error\n}\n\nfunc (m *mock) endpoint(context.Context, interface{}) (interface{}, error) {\n\tm.through++\n\treturn struct{}{}, m.err\n}\n"
  },
  {
    "path": "codecov.yml",
    "content": "comment: false\n"
  },
  {
    "path": "docker-compose-integration.yml",
    "content": "version: '2'\nservices:\n  etcd:\n    image: gcr.io/etcd-development/etcd:v3.5.0\n    ports:\n      - \"2379:2379\"\n    environment:\n      ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379\n      ETCD_ADVERTISE_CLIENT_URLS: http://0.0.0.0:2379\n\n  consul:\n    image: consul:1.7\n    ports:\n      - \"8500:8500\"\n\n  zk:\n    image: zookeeper:3.5\n    ports:\n      - \"2181:2181\"\n\n  eureka:\n    image: springcloud/eureka\n    environment:\n      eureka.server.responseCacheUpdateIntervalMs: 1000\n    ports:\n      - \"8761:8761\"\n"
  },
  {
    "path": "endpoint/doc.go",
    "content": "// Package endpoint defines an abstraction for RPCs.\n//\n// Endpoints are a fundamental building block for many Go kit components.\n// Endpoints are implemented by servers, and called by clients.\npackage endpoint\n"
  },
  {
    "path": "endpoint/endpoint.go",
    "content": "package endpoint\n\nimport (\n\t\"context\"\n)\n\n// Endpoint is the fundamental building block of servers and clients.\n// It represents a single RPC method.\ntype Endpoint func(ctx context.Context, request interface{}) (response interface{}, err error)\n\n// Nop is an endpoint that does nothing and returns a nil error.\n// Useful for tests.\nfunc Nop(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil }\n\n// Middleware is a chainable behavior modifier for endpoints.\ntype Middleware func(Endpoint) Endpoint\n\n// Chain is a helper function for composing middlewares. Requests will\n// traverse them in the order they're declared. That is, the first middleware\n// is treated as the outermost middleware.\nfunc Chain(outer Middleware, others ...Middleware) Middleware {\n\treturn func(next Endpoint) Endpoint {\n\t\tfor i := len(others) - 1; i >= 0; i-- { // reverse\n\t\t\tnext = others[i](next)\n\t\t}\n\t\treturn outer(next)\n\t}\n}\n\n// Failer may be implemented by Go kit response types that contain business\n// logic error details. If Failed returns a non-nil error, the Go kit transport\n// layer may interpret this as a business logic error, and may encode it\n// differently than a regular, successful response.\n//\n// It's not necessary for your response types to implement Failer, but it may\n// help for more sophisticated use cases. The addsvc example shows how Failer\n// should be used by a complete application.\ntype Failer interface {\n\tFailed() error\n}\n"
  },
  {
    "path": "endpoint/endpoint_example_test.go",
    "content": "package endpoint_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n)\n\nfunc ExampleChain() {\n\te := endpoint.Chain(\n\t\tannotate(\"first\"),\n\t\tannotate(\"second\"),\n\t\tannotate(\"third\"),\n\t)(myEndpoint)\n\n\tif _, err := e(ctx, req); err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Output:\n\t// first pre\n\t// second pre\n\t// third pre\n\t// my endpoint!\n\t// third post\n\t// second post\n\t// first post\n}\n\nvar (\n\tctx = context.Background()\n\treq = struct{}{}\n)\n\nfunc annotate(s string) endpoint.Middleware {\n\treturn func(next endpoint.Endpoint) endpoint.Endpoint {\n\t\treturn func(ctx context.Context, request interface{}) (interface{}, error) {\n\t\t\tfmt.Println(s, \"pre\")\n\t\t\tdefer fmt.Println(s, \"post\")\n\t\t\treturn next(ctx, request)\n\t\t}\n\t}\n}\n\nfunc myEndpoint(context.Context, interface{}) (interface{}, error) {\n\tfmt.Println(\"my endpoint!\")\n\treturn struct{}{}, nil\n}\n"
  },
  {
    "path": "examples/README.md",
    "content": "# Examples\n\nExamples have been relocated to a separate repository: https://github.com/go-kit/examples\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/go-kit/kit\n\ngo 1.17\n\nrequire (\n\tgithub.com/VividCortex/gohistogram v1.0.0\n\tgithub.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5\n\tgithub.com/aws/aws-sdk-go v1.40.45\n\tgithub.com/aws/aws-sdk-go-v2 v1.9.1\n\tgithub.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1\n\tgithub.com/casbin/casbin/v2 v2.37.0\n\tgithub.com/go-kit/log v0.2.0\n\tgithub.com/go-zookeeper/zk v1.0.2\n\tgithub.com/golang-jwt/jwt/v4 v4.0.0\n\tgithub.com/hashicorp/consul/api v1.14.0\n\tgithub.com/hudl/fargo v1.4.0\n\tgithub.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab\n\tgithub.com/nats-io/nats-server/v2 v2.8.4\n\tgithub.com/nats-io/nats.go v1.15.0\n\tgithub.com/opentracing/opentracing-go v1.2.0\n\tgithub.com/openzipkin/zipkin-go v0.2.5\n\tgithub.com/performancecopilot/speed/v4 v4.0.0\n\tgithub.com/prometheus/client_golang v1.11.1\n\tgithub.com/rabbitmq/amqp091-go v1.2.0\n\tgithub.com/sirupsen/logrus v1.8.1\n\tgithub.com/sony/gobreaker v0.4.1\n\tgithub.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e\n\tgo.etcd.io/etcd/client/pkg/v3 v3.5.0\n\tgo.etcd.io/etcd/client/v2 v2.305.0\n\tgo.etcd.io/etcd/client/v3 v3.5.0\n\tgo.opencensus.io v0.23.0\n\tgo.uber.org/zap v1.19.1\n\tgolang.org/x/sync v0.0.0-20210220032951-036812b2e83c\n\tgolang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11\n\tgoogle.golang.org/grpc v1.40.0\n\tgoogle.golang.org/protobuf v1.27.1\n)\n\nrequire (\n\tgithub.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect\n\tgithub.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect\n\tgithub.com/armon/go-metrics v0.4.0 // indirect\n\tgithub.com/aws/smithy-go v1.8.0 // indirect\n\tgithub.com/beorn7/perks v1.0.1 // indirect\n\tgithub.com/cenkalti/backoff/v4 v4.1.1 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.1.2 // indirect\n\tgithub.com/clbanning/mxj v1.8.4 // indirect\n\tgithub.com/coreos/go-semver v0.3.0 // indirect\n\tgithub.com/coreos/go-systemd/v22 v22.3.2 // indirect\n\tgithub.com/edsrzf/mmap-go v1.0.0 // indirect\n\tgithub.com/fatih/color v1.13.0 // indirect\n\tgithub.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8 // indirect\n\tgithub.com/go-logfmt/logfmt v0.5.1 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgithub.com/hashicorp/go-cleanhttp v0.5.2 // indirect\n\tgithub.com/hashicorp/go-hclog v1.2.2 // indirect\n\tgithub.com/hashicorp/go-immutable-radix v1.3.1 // indirect\n\tgithub.com/hashicorp/go-rootcerts v1.0.2 // indirect\n\tgithub.com/hashicorp/golang-lru v0.5.4 // indirect\n\tgithub.com/hashicorp/serf v0.10.0 // indirect\n\tgithub.com/jmespath/go-jmespath v0.4.0 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/klauspost/compress v1.14.4 // indirect\n\tgithub.com/mattn/go-colorable v0.1.13 // indirect\n\tgithub.com/mattn/go-isatty v0.0.16 // indirect\n\tgithub.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect\n\tgithub.com/miekg/dns v1.1.43 // indirect\n\tgithub.com/minio/highwayhash v1.0.2 // indirect\n\tgithub.com/mitchellh/go-homedir v1.1.0 // indirect\n\tgithub.com/mitchellh/mapstructure v1.5.0 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect\n\tgithub.com/modern-go/reflect2 v1.0.2 // indirect\n\tgithub.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a // indirect\n\tgithub.com/nats-io/nkeys v0.3.0 // indirect\n\tgithub.com/nats-io/nuid v1.0.1 // indirect\n\tgithub.com/op/go-logging v0.0.0-20160315200505-970db520ece7 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/prometheus/client_model v0.2.0 // indirect\n\tgithub.com/prometheus/common v0.30.0 // indirect\n\tgithub.com/prometheus/procfs v0.7.3 // indirect\n\tgo.etcd.io/etcd/api/v3 v3.5.0 // indirect\n\tgo.uber.org/atomic v1.9.0 // indirect\n\tgo.uber.org/multierr v1.7.0 // indirect\n\tgolang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect\n\tgolang.org/x/net v0.0.0-20211216030914-fe4d6282115f // indirect\n\tgolang.org/x/sys v0.0.0-20220823224334-20c2bfdbfe24 // indirect\n\tgolang.org/x/text v0.3.7 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20210917145530-b395a37504d4 // indirect\n\tgopkg.in/gcfg.v1 v1.2.3 // indirect\n\tgopkg.in/warnings.v0 v0.1.2 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=\ncloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=\ncloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=\ncloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=\ncloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=\ncloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=\ncloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=\ncloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=\ncloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=\ncloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=\ncloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=\ncloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=\ncloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=\ncloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ncloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=\ncloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=\ncloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=\ncloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=\ngithub.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=\ngithub.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM=\ngithub.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=\ngithub.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=\ngithub.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=\ngithub.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=\ngithub.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE=\ngithub.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=\ngithub.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw=\ngithub.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=\ngithub.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=\ngithub.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8Q=\ngithub.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/aws/aws-sdk-go v1.40.45 h1:QN1nsY27ssD/JmW4s83qmSb+uL6DG4GmCDzjmJB4xUI=\ngithub.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=\ngithub.com/aws/aws-sdk-go-v2 v1.9.1 h1:ZbovGV/qo40nrOJ4q8G33AGICzaPI45FHQWJ9650pF4=\ngithub.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=\ngithub.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1 h1:w/fPGB0t5rWwA43mux4e9ozFSH5zF1moQemlA131PWc=\ngithub.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=\ngithub.com/aws/smithy-go v1.8.0 h1:AEwwwXQZtUwP5Mz506FeXXrKBe0jA8gVM+1gEcSRooc=\ngithub.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=\ngithub.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=\ngithub.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/casbin/casbin/v2 v2.37.0 h1:/poEwPSovi4bTOcP752/CsTQiRz2xycyVKFG7GUhbDw=\ngithub.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=\ngithub.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ=\ngithub.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=\ngithub.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=\ngithub.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=\ngithub.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=\ngithub.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=\ngithub.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=\ngithub.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=\ngithub.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=\ngithub.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=\ngithub.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=\ngithub.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=\ngithub.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=\ngithub.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=\ngithub.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=\ngithub.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2 h1:cZqz+yOJ/R64LcKjNQOdARott/jP7BnUQ9Ah7KaZCvw=\ngithub.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo=\ngithub.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8 h1:a9ENSRDFBUPkJ5lCgVZh26+ZbGyoVJG7yb5SSzF5H54=\ngithub.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=\ngithub.com/go-kit/log v0.2.0 h1:7i2K3eKTos3Vc0enKCfnVcgHh2olr/MyfboYq7cAcFw=\ngithub.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=\ngithub.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=\ngithub.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=\ngithub.com/go-zookeeper/zk v1.0.2 h1:4mx0EYENAdX/B/rbunjlt5+4RTA/a9SMHBRuSKdGxPM=\ngithub.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=\ngithub.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang-jwt/jwt/v4 v4.0.0 h1:RAqyYixv1p7uEnocuy8P1nru5wprCh/MH2BIlW5z5/o=\ngithub.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=\ngithub.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=\ngithub.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=\ngithub.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=\ngithub.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=\ngithub.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=\ngithub.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=\ngithub.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/hashicorp/consul/api v1.14.0 h1:Y64GIJ8hYTu+tuGekwO4G4ardXoiCivX9wv1iP/kihk=\ngithub.com/hashicorp/consul/api v1.14.0/go.mod h1:bcaw5CSZ7NE9qfOfKCI1xb7ZKjzu/MyvQkCLTfqLqxQ=\ngithub.com/hashicorp/consul/sdk v0.10.0 h1:rGLEh2AWK4K0KCMvqWAz2EYxQqgciIfMagWZ0nVe5MI=\ngithub.com/hashicorp/consul/sdk v0.10.0/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw=\ngithub.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=\ngithub.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=\ngithub.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=\ngithub.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=\ngithub.com/hashicorp/go-hclog v1.2.2 h1:ihRI7YFwcZdiSD7SIenIhHfQH3OuDvWerAUBZbeQS3M=\ngithub.com/hashicorp/go-hclog v1.2.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=\ngithub.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=\ngithub.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=\ngithub.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=\ngithub.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=\ngithub.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=\ngithub.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=\ngithub.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=\ngithub.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=\ngithub.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=\ngithub.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=\ngithub.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=\ngithub.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=\ngithub.com/hashicorp/memberlist v0.3.1/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=\ngithub.com/hashicorp/memberlist v0.4.0 h1:k3uda5gZcltmafuFF+UFqNEl5PrH+yPZ4zkjp1f/H/8=\ngithub.com/hashicorp/memberlist v0.4.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=\ngithub.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=\ngithub.com/hashicorp/serf v0.10.0 h1:89qvvpfMQnz6c2y4pv7j2vUUmeT1+5TSZMexuTbtsPs=\ngithub.com/hashicorp/serf v0.10.0/go.mod h1:bXN03oZc5xlH46k/K1qTrpXb9ERKyY1/i/N5mxvgrZw=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/hudl/fargo v1.4.0 h1:ZDDILMbB37UlAVLlWcJ2Iz1XuahZZTDZfdCKeclfq2s=\ngithub.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab h1:HqW4xhhynfjrtEiiSGcQUd6vrK23iMam1FO8rI7mwig=\ngithub.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=\ngithub.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=\ngithub.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=\ngithub.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=\ngithub.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=\ngithub.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=\ngithub.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/klauspost/compress v1.14.4 h1:eijASRJcobkVtSt81Olfh7JX43osYLwy5krOJo6YEu4=\ngithub.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=\ngithub.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=\ngithub.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=\ngithub.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=\ngithub.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=\ngithub.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=\ngithub.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=\ngithub.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=\ngithub.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=\ngithub.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=\ngithub.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=\ngithub.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=\ngithub.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=\ngithub.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a h1:lem6QCvxR0Y28gth9P+wV2K/zYUUAkJ+55U8cpS0p5I=\ngithub.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k=\ngithub.com/nats-io/nats-server/v2 v2.8.4 h1:0jQzze1T9mECg8YZEl8+WYUXb9JKluJfCBriPUtluB4=\ngithub.com/nats-io/nats-server/v2 v2.8.4/go.mod h1:8zZa+Al3WsESfmgSs98Fi06dRWLH5Bnq90m5bKD/eT4=\ngithub.com/nats-io/nats.go v1.15.0 h1:3IXNBolWrwIUf2soxh6Rla8gPzYWEZQBUBK6RV21s+o=\ngithub.com/nats-io/nats.go v1.15.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=\ngithub.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8=\ngithub.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=\ngithub.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=\ngithub.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=\ngithub.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=\ngithub.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=\ngithub.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=\ngithub.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=\ngithub.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=\ngithub.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=\ngithub.com/openzipkin/zipkin-go v0.2.5 h1:UwtQQx2pyPIgWYHRg+epgdx1/HnBQTgN3/oIYEJTQzU=\ngithub.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=\ngithub.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/performancecopilot/speed/v4 v4.0.0 h1:VxEDCmdkfbQYDlcr/GC9YoN9PQ6p8ulk9xVsepYy9ZY=\ngithub.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM=\ngithub.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=\ngithub.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=\ngithub.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=\ngithub.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=\ngithub.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s=\ngithub.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=\ngithub.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=\ngithub.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=\ngithub.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=\ngithub.com/prometheus/common v0.30.0 h1:JEkYlQnpzrzQFxi6gnukFPdQ+ac82oRhzMcIduJu/Ug=\ngithub.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=\ngithub.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=\ngithub.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=\ngithub.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/rabbitmq/amqp091-go v1.2.0 h1:1pHBxAsQh54R9eX/xo679fUEAfv3loMqi0pvRFOj2nk=\ngithub.com/rabbitmq/amqp091-go v1.2.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM=\ngithub.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=\ngithub.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/sony/gobreaker v0.4.1 h1:oMnRNZXX5j85zso6xCPRNPtmAycat+WcoKbklScLDgQ=\ngithub.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=\ngithub.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e h1:mOtuXaRAbVZsxAHVdPR3IjfmN8T1h2iczJLynhLybf8=\ngithub.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=\ngithub.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=\ngithub.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=\ngithub.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.etcd.io/etcd/api/v3 v3.5.0 h1:GsV3S+OfZEOCNXdtNkBSR7kgLobAa/SO6tCxRa0GAYw=\ngo.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.0 h1:2aQv6F436YnN7I4VbI8PPYrBhu+SmrTaADcf8Mi/6PU=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=\ngo.etcd.io/etcd/client/v2 v2.305.0 h1:ftQ0nOOHMcbMS3KIaDQ0g5Qcd6bhaBrQT6b89DfwLTs=\ngo.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=\ngo.etcd.io/etcd/client/v3 v3.5.0 h1:62Eh0XOro+rDwkrypAGDfgmNh5Joq+z+W9HZdlXMzek=\ngo.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=\ngo.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=\ngo.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/goleak v1.1.11-0.20210813005559-691160354723 h1:sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4=\ngo.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\ngo.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=\ngo.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=\ngo.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=\ngo.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=\ngo.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=\ngolang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=\ngolang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=\ngolang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y=\ngolang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\ngolang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=\ngolang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=\ngolang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM=\ngolang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220823224334-20c2bfdbfe24 h1:TyKJRhyo17yWxOMCTHKWrc5rddHORMlnZ/j57umaUd8=\ngolang.org/x/sys v0.0.0-20220823224334-20c2bfdbfe24/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M=\ngolang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=\ngonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM=\ngonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=\ngonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=\ngonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=\ngoogle.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=\ngoogle.golang.org/genproto v0.0.0-20210917145530-b395a37504d4 h1:ysnBoUyeL/H6RCvNRhWHjKoDEmguI+mPU+qHgK8qv/w=\ngoogle.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=\ngoogle.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q=\ngoogle.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=\ngoogle.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs=\ngopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=\ngopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nhonnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nhonnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nrsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\nsigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=\n"
  },
  {
    "path": "lint",
    "content": "#!/usr/bin/env bash\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nif [ ! $(command -v gometalinter) ]\nthen\n\tgo get github.com/alecthomas/gometalinter\n\tgometalinter --update --install\nfi\n\ntime gometalinter \\\n\t--exclude='error return value not checked.*(Close|Log|Print).*\\(errcheck\\)$' \\\n\t--exclude='.*_test\\.go:.*error return value not checked.*\\(errcheck\\)$' \\\n\t--exclude='/thrift/' \\\n\t--exclude='/pb/' \\\n\t--exclude='no args in Log call \\(vet\\)' \\\n\t--disable=dupl \\\n\t--disable=aligncheck \\\n\t--disable=gotype \\\n\t--cyclo-over=20 \\\n\t--tests \\\n\t--concurrency=2 \\\n\t--deadline=300s \\\n\t./...\n"
  },
  {
    "path": "log/README.md",
    "content": "# package log\n\n**Deprecation notice:** The core Go kit log packages (log, log/level, log/term, and\nlog/syslog) have been moved to their own repository at github.com/go-kit/log.\nThe corresponding packages in this directory remain for backwards compatibility.\nTheir types alias the types and their functions call the functions provided by\nthe new repository. Using either import path should be equivalent. Prefer the\nnew import path when practical.\n\n______\n\n`package log` provides a minimal interface for structured logging in services.\nIt may be wrapped to encode conventions, enforce type-safety, provide leveled\nlogging, and so on. It can be used for both typical application log events,\nand log-structured data streams.\n\n## Structured logging\n\nStructured logging is, basically, conceding to the reality that logs are\n_data_, and warrant some level of schematic rigor. Using a stricter,\nkey/value-oriented message format for our logs, containing contextual and\nsemantic information, makes it much easier to get insight into the\noperational activity of the systems we build. Consequently, `package log` is\nof the strong belief that \"[the benefits of structured logging outweigh the\nminimal effort involved](https://www.thoughtworks.com/radar/techniques/structured-logging)\".\n\nMigrating from unstructured to structured logging is probably a lot easier\nthan you'd expect.\n\n```go\n// Unstructured\nlog.Printf(\"HTTP server listening on %s\", addr)\n\n// Structured\nlogger.Log(\"transport\", \"HTTP\", \"addr\", addr, \"msg\", \"listening\")\n```\n\n## Usage\n\n### Typical application logging\n\n```go\nw := log.NewSyncWriter(os.Stderr)\nlogger := log.NewLogfmtLogger(w)\nlogger.Log(\"question\", \"what is the meaning of life?\", \"answer\", 42)\n\n// Output:\n// question=\"what is the meaning of life?\" answer=42\n```\n\n### Contextual Loggers\n\n```go\nfunc main() {\n\tvar logger log.Logger\n\tlogger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))\n\tlogger = log.With(logger, \"instance_id\", 123)\n\n\tlogger.Log(\"msg\", \"starting\")\n\tNewWorker(log.With(logger, \"component\", \"worker\")).Run()\n\tNewSlacker(log.With(logger, \"component\", \"slacker\")).Run()\n}\n\n// Output:\n// instance_id=123 msg=starting\n// instance_id=123 component=worker msg=running\n// instance_id=123 component=slacker msg=running\n```\n\n### Interact with stdlib logger\n\nRedirect stdlib logger to Go kit logger.\n\n```go\nimport (\n\t\"os\"\n\tstdlog \"log\"\n\tkitlog \"github.com/go-kit/kit/log\"\n)\n\nfunc main() {\n\tlogger := kitlog.NewJSONLogger(kitlog.NewSyncWriter(os.Stdout))\n\tstdlog.SetOutput(kitlog.NewStdlibAdapter(logger))\n\tstdlog.Print(\"I sure like pie\")\n}\n\n// Output:\n// {\"msg\":\"I sure like pie\",\"ts\":\"2016/01/01 12:34:56\"}\n```\n\nOr, if, for legacy reasons, you need to pipe all of your logging through the\nstdlib log package, you can redirect Go kit logger to the stdlib logger.\n\n```go\nlogger := kitlog.NewLogfmtLogger(kitlog.StdlibWriter{})\nlogger.Log(\"legacy\", true, \"msg\", \"at least it's something\")\n\n// Output:\n// 2016/01/01 12:34:56 legacy=true msg=\"at least it's something\"\n```\n\n### Timestamps and callers\n\n```go\nvar logger log.Logger\nlogger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))\nlogger = log.With(logger, \"ts\", log.DefaultTimestampUTC, \"caller\", log.DefaultCaller)\n\nlogger.Log(\"msg\", \"hello\")\n\n// Output:\n// ts=2016-01-01T12:34:56Z caller=main.go:15 msg=hello\n```\n\n## Levels\n\nLog levels are supported via the [level package](https://godoc.org/github.com/go-kit/kit/log/level).\n\n## Supported output formats\n\n- [Logfmt](https://brandur.org/logfmt) ([see also](https://blog.codeship.com/logfmt-a-log-format-thats-easy-to-read-and-write))\n- JSON\n\n## Enhancements\n\n`package log` is centered on the one-method Logger interface.\n\n```go\ntype Logger interface {\n\tLog(keyvals ...interface{}) error\n}\n```\n\nThis interface, and its supporting code like is the product of much iteration\nand evaluation. For more details on the evolution of the Logger interface,\nsee [The Hunt for a Logger Interface](http://go-talks.appspot.com/github.com/ChrisHines/talks/structured-logging/structured-logging.slide#1),\na talk by [Chris Hines](https://github.com/ChrisHines).\nAlso, please see\n[#63](https://github.com/go-kit/kit/issues/63),\n[#76](https://github.com/go-kit/kit/pull/76),\n[#131](https://github.com/go-kit/kit/issues/131),\n[#157](https://github.com/go-kit/kit/pull/157),\n[#164](https://github.com/go-kit/kit/issues/164), and\n[#252](https://github.com/go-kit/kit/pull/252)\nto review historical conversations about package log and the Logger interface.\n\nValue-add packages and suggestions,\nlike improvements to [the leveled logger](https://godoc.org/github.com/go-kit/kit/log/level),\nare of course welcome. Good proposals should\n\n- Be composable with [contextual loggers](https://godoc.org/github.com/go-kit/kit/log#With),\n- Not break the behavior of [log.Caller](https://godoc.org/github.com/go-kit/kit/log#Caller) in any wrapped contextual loggers, and\n- Be friendly to packages that accept only an unadorned log.Logger.\n\n## Benchmarks & comparisons\n\nThere are a few Go logging benchmarks and comparisons that include Go kit's package log.\n\n- [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench) includes kit/log\n- [uber-common/zap](https://github.com/uber-common/zap), a zero-alloc logging library, includes a comparison with kit/log\n"
  },
  {
    "path": "log/deprecated_levels/levels.go",
    "content": "// Package levels implements leveled logging on top of Go kit's log package.\n//\n// Deprecated: Use github.com/go-kit/log/level instead.\npackage levels\n\nimport \"github.com/go-kit/log\"\n\n// Levels provides a leveled logging wrapper around a logger. It has five\n// levels: debug, info, warning (warn), error, and critical (crit). If you\n// want a different set of levels, you can create your own levels type very\n// easily, and you can elide the configuration.\ntype Levels struct {\n\tlogger   log.Logger\n\tlevelKey string\n\n\t// We have a choice between storing level values in string fields or\n\t// making a separate context for each level. When using string fields the\n\t// Log method must combine the base context, the level data, and the\n\t// logged keyvals; but the With method only requires updating one context.\n\t// If we instead keep a separate context for each level the Log method\n\t// must only append the new keyvals; but the With method would have to\n\t// update all five contexts.\n\n\t// Roughly speaking, storing multiple contexts breaks even if the ratio of\n\t// Log/With calls is more than the number of levels. We have chosen to\n\t// make the With method cheap and the Log method a bit more costly because\n\t// we do not expect most applications to Log more than five times for each\n\t// call to With.\n\n\tdebugValue string\n\tinfoValue  string\n\twarnValue  string\n\terrorValue string\n\tcritValue  string\n}\n\n// New creates a new leveled logger, wrapping the passed logger.\nfunc New(logger log.Logger, options ...Option) Levels {\n\tl := Levels{\n\t\tlogger:   logger,\n\t\tlevelKey: \"level\",\n\n\t\tdebugValue: \"debug\",\n\t\tinfoValue:  \"info\",\n\t\twarnValue:  \"warn\",\n\t\terrorValue: \"error\",\n\t\tcritValue:  \"crit\",\n\t}\n\tfor _, option := range options {\n\t\toption(&l)\n\t}\n\treturn l\n}\n\n// With returns a new leveled logger that includes keyvals in all log events.\nfunc (l Levels) With(keyvals ...interface{}) Levels {\n\treturn Levels{\n\t\tlogger:     log.With(l.logger, keyvals...),\n\t\tlevelKey:   l.levelKey,\n\t\tdebugValue: l.debugValue,\n\t\tinfoValue:  l.infoValue,\n\t\twarnValue:  l.warnValue,\n\t\terrorValue: l.errorValue,\n\t\tcritValue:  l.critValue,\n\t}\n}\n\n// Debug returns a debug level logger.\nfunc (l Levels) Debug() log.Logger {\n\treturn log.WithPrefix(l.logger, l.levelKey, l.debugValue)\n}\n\n// Info returns an info level logger.\nfunc (l Levels) Info() log.Logger {\n\treturn log.WithPrefix(l.logger, l.levelKey, l.infoValue)\n}\n\n// Warn returns a warning level logger.\nfunc (l Levels) Warn() log.Logger {\n\treturn log.WithPrefix(l.logger, l.levelKey, l.warnValue)\n}\n\n// Error returns an error level logger.\nfunc (l Levels) Error() log.Logger {\n\treturn log.WithPrefix(l.logger, l.levelKey, l.errorValue)\n}\n\n// Crit returns a critical level logger.\nfunc (l Levels) Crit() log.Logger {\n\treturn log.WithPrefix(l.logger, l.levelKey, l.critValue)\n}\n\n// Option sets a parameter for leveled loggers.\ntype Option func(*Levels)\n\n// Key sets the key for the field used to indicate log level. By default,\n// the key is \"level\".\nfunc Key(key string) Option {\n\treturn func(l *Levels) { l.levelKey = key }\n}\n\n// DebugValue sets the value for the field used to indicate the debug log\n// level. By default, the value is \"debug\".\nfunc DebugValue(value string) Option {\n\treturn func(l *Levels) { l.debugValue = value }\n}\n\n// InfoValue sets the value for the field used to indicate the info log level.\n// By default, the value is \"info\".\nfunc InfoValue(value string) Option {\n\treturn func(l *Levels) { l.infoValue = value }\n}\n\n// WarnValue sets the value for the field used to indicate the warning log\n// level. By default, the value is \"warn\".\nfunc WarnValue(value string) Option {\n\treturn func(l *Levels) { l.warnValue = value }\n}\n\n// ErrorValue sets the value for the field used to indicate the error log\n// level. By default, the value is \"error\".\nfunc ErrorValue(value string) Option {\n\treturn func(l *Levels) { l.errorValue = value }\n}\n\n// CritValue sets the value for the field used to indicate the critical log\n// level. By default, the value is \"crit\".\nfunc CritValue(value string) Option {\n\treturn func(l *Levels) { l.critValue = value }\n}\n"
  },
  {
    "path": "log/deprecated_levels/levels_test.go",
    "content": "package levels_test\n\nimport (\n\t\"bytes\"\n\t\"os\"\n\t\"testing\"\n\n\tlevels \"github.com/go-kit/kit/log/deprecated_levels\"\n\t\"github.com/go-kit/log\"\n)\n\nfunc TestDefaultLevels(t *testing.T) {\n\tbuf := bytes.Buffer{}\n\tlogger := levels.New(log.NewLogfmtLogger(&buf))\n\n\tlogger.Debug().Log(\"msg\", \"résumé\") // of course you'd want to do this\n\tif want, have := \"level=debug msg=résumé\\n\", buf.String(); want != have {\n\t\tt.Errorf(\"want %#v, have %#v\", want, have)\n\t}\n\n\tbuf.Reset()\n\tlogger.Info().Log(\"msg\", \"Åhus\")\n\tif want, have := \"level=info msg=Åhus\\n\", buf.String(); want != have {\n\t\tt.Errorf(\"want %#v, have %#v\", want, have)\n\t}\n\n\tbuf.Reset()\n\tlogger.Error().Log(\"msg\", \"© violation\")\n\tif want, have := \"level=error msg=\\\"© violation\\\"\\n\", buf.String(); want != have {\n\t\tt.Errorf(\"want %#v, have %#v\", want, have)\n\t}\n\n\tbuf.Reset()\n\tlogger.Crit().Log(\"msg\", \"\t\")\n\tif want, have := \"level=crit msg=\\\"\\\\t\\\"\\n\", buf.String(); want != have {\n\t\tt.Errorf(\"want %#v, have %#v\", want, have)\n\t}\n}\n\nfunc TestModifiedLevels(t *testing.T) {\n\tbuf := bytes.Buffer{}\n\tlogger := levels.New(\n\t\tlog.NewJSONLogger(&buf),\n\t\tlevels.Key(\"l\"),\n\t\tlevels.DebugValue(\"dbg\"),\n\t\tlevels.InfoValue(\"nfo\"),\n\t\tlevels.WarnValue(\"wrn\"),\n\t\tlevels.ErrorValue(\"err\"),\n\t\tlevels.CritValue(\"crt\"),\n\t)\n\tlogger.With(\"easter_island\", \"176°\").Debug().Log(\"msg\", \"moai\")\n\tif want, have := `{\"easter_island\":\"176°\",\"l\":\"dbg\",\"msg\":\"moai\"}`+\"\\n\", buf.String(); want != have {\n\t\tt.Errorf(\"want %#v, have %#v\", want, have)\n\t}\n}\n\nfunc ExampleLevels() {\n\tlogger := levels.New(log.NewLogfmtLogger(os.Stdout))\n\tlogger.Debug().Log(\"msg\", \"hello\")\n\tlogger.With(\"context\", \"foo\").Warn().Log(\"err\", \"error\")\n\n\t// Output:\n\t// level=debug msg=hello\n\t// level=warn context=foo err=error\n}\n"
  },
  {
    "path": "log/doc.go",
    "content": "// Package log provides a structured logger.\n//\n// Deprecated: Use github.com/go-kit/log instead.\n//\n// Structured logging produces logs easily consumed later by humans or\n// machines. Humans might be interested in debugging errors, or tracing\n// specific requests. Machines might be interested in counting interesting\n// events, or aggregating information for off-line processing. In both cases,\n// it is important that the log messages are structured and actionable.\n// Package log is designed to encourage both of these best practices.\n//\n// Basic Usage\n//\n// The fundamental interface is Logger. Loggers create log events from\n// key/value data. The Logger interface has a single method, Log, which\n// accepts a sequence of alternating key/value pairs, which this package names\n// keyvals.\n//\n//    type Logger interface {\n//        Log(keyvals ...interface{}) error\n//    }\n//\n// Here is an example of a function using a Logger to create log events.\n//\n//    func RunTask(task Task, logger log.Logger) string {\n//        logger.Log(\"taskID\", task.ID, \"event\", \"starting task\")\n//        ...\n//        logger.Log(\"taskID\", task.ID, \"event\", \"task complete\")\n//    }\n//\n// The keys in the above example are \"taskID\" and \"event\". The values are\n// task.ID, \"starting task\", and \"task complete\". Every key is followed\n// immediately by its value.\n//\n// Keys are usually plain strings. Values may be any type that has a sensible\n// encoding in the chosen log format. With structured logging it is a good\n// idea to log simple values without formatting them. This practice allows\n// the chosen logger to encode values in the most appropriate way.\n//\n// Contextual Loggers\n//\n// A contextual logger stores keyvals that it includes in all log events.\n// Building appropriate contextual loggers reduces repetition and aids\n// consistency in the resulting log output. With, WithPrefix, and WithSuffix\n// add context to a logger. We can use With to improve the RunTask example.\n//\n//    func RunTask(task Task, logger log.Logger) string {\n//        logger = log.With(logger, \"taskID\", task.ID)\n//        logger.Log(\"event\", \"starting task\")\n//        ...\n//        taskHelper(task.Cmd, logger)\n//        ...\n//        logger.Log(\"event\", \"task complete\")\n//    }\n//\n// The improved version emits the same log events as the original for the\n// first and last calls to Log. Passing the contextual logger to taskHelper\n// enables each log event created by taskHelper to include the task.ID even\n// though taskHelper does not have access to that value. Using contextual\n// loggers this way simplifies producing log output that enables tracing the\n// life cycle of individual tasks. (See the Contextual example for the full\n// code of the above snippet.)\n//\n// Dynamic Contextual Values\n//\n// A Valuer function stored in a contextual logger generates a new value each\n// time an event is logged. The Valuer example demonstrates how this feature\n// works.\n//\n// Valuers provide the basis for consistently logging timestamps and source\n// code location. The log package defines several valuers for that purpose.\n// See Timestamp, DefaultTimestamp, DefaultTimestampUTC, Caller, and\n// DefaultCaller. A common logger initialization sequence that ensures all log\n// entries contain a timestamp and source location looks like this:\n//\n//    logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout))\n//    logger = log.With(logger, \"ts\", log.DefaultTimestampUTC, \"caller\", log.DefaultCaller)\n//\n// Concurrent Safety\n//\n// Applications with multiple goroutines want each log event written to the\n// same logger to remain separate from other log events. Package log provides\n// two simple solutions for concurrent safe logging.\n//\n// NewSyncWriter wraps an io.Writer and serializes each call to its Write\n// method. Using a SyncWriter has the benefit that the smallest practical\n// portion of the logging logic is performed within a mutex, but it requires\n// the formatting Logger to make only one call to Write per log event.\n//\n// NewSyncLogger wraps any Logger and serializes each call to its Log method.\n// Using a SyncLogger has the benefit that it guarantees each log event is\n// handled atomically within the wrapped logger, but it typically serializes\n// both the formatting and output logic. Use a SyncLogger if the formatting\n// logger may perform multiple writes per log event.\n//\n// Error Handling\n//\n// This package relies on the practice of wrapping or decorating loggers with\n// other loggers to provide composable pieces of functionality. It also means\n// that Logger.Log must return an error because some\n// implementations—especially those that output log data to an io.Writer—may\n// encounter errors that cannot be handled locally. This in turn means that\n// Loggers that wrap other loggers should return errors from the wrapped\n// logger up the stack.\n//\n// Fortunately, the decorator pattern also provides a way to avoid the\n// necessity to check for errors every time an application calls Logger.Log.\n// An application required to panic whenever its Logger encounters\n// an error could initialize its logger as follows.\n//\n//    fmtlogger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout))\n//    logger := log.LoggerFunc(func(keyvals ...interface{}) error {\n//        if err := fmtlogger.Log(keyvals...); err != nil {\n//            panic(err)\n//        }\n//        return nil\n//    })\npackage log\n"
  },
  {
    "path": "log/example_test.go",
    "content": "package log_test\n\nimport (\n\t\"math/rand\"\n\t\"os\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/log\"\n)\n\nfunc Example_basic() {\n\tlogger := log.NewLogfmtLogger(os.Stdout)\n\n\ttype Task struct {\n\t\tID int\n\t}\n\n\tRunTask := func(task Task, logger log.Logger) {\n\t\tlogger.Log(\"taskID\", task.ID, \"event\", \"starting task\")\n\n\t\tlogger.Log(\"taskID\", task.ID, \"event\", \"task complete\")\n\t}\n\n\tRunTask(Task{ID: 1}, logger)\n\n\t// Output:\n\t// taskID=1 event=\"starting task\"\n\t// taskID=1 event=\"task complete\"\n}\n\nfunc Example_contextual() {\n\tlogger := log.NewLogfmtLogger(os.Stdout)\n\n\ttype Task struct {\n\t\tID  int\n\t\tCmd string\n\t}\n\n\ttaskHelper := func(cmd string, logger log.Logger) {\n\t\t// execute(cmd)\n\t\tlogger.Log(\"cmd\", cmd, \"dur\", 42*time.Millisecond)\n\t}\n\n\tRunTask := func(task Task, logger log.Logger) {\n\t\tlogger = log.With(logger, \"taskID\", task.ID)\n\t\tlogger.Log(\"event\", \"starting task\")\n\n\t\ttaskHelper(task.Cmd, logger)\n\n\t\tlogger.Log(\"event\", \"task complete\")\n\t}\n\n\tRunTask(Task{ID: 1, Cmd: \"echo Hello, world!\"}, logger)\n\n\t// Output:\n\t// taskID=1 event=\"starting task\"\n\t// taskID=1 cmd=\"echo Hello, world!\" dur=42ms\n\t// taskID=1 event=\"task complete\"\n}\n\nfunc Example_valuer() {\n\tlogger := log.NewLogfmtLogger(os.Stdout)\n\n\tcount := 0\n\tcounter := func() interface{} {\n\t\tcount++\n\t\treturn count\n\t}\n\n\tlogger = log.With(logger, \"count\", log.Valuer(counter))\n\n\tlogger.Log(\"call\", \"first\")\n\tlogger.Log(\"call\", \"second\")\n\n\t// Output:\n\t// count=1 call=first\n\t// count=2 call=second\n}\n\nfunc Example_debugInfo() {\n\tlogger := log.NewLogfmtLogger(os.Stdout)\n\n\t// make time predictable for this test\n\tbaseTime := time.Date(2015, time.February, 3, 10, 0, 0, 0, time.UTC)\n\tmockTime := func() time.Time {\n\t\tbaseTime = baseTime.Add(time.Second)\n\t\treturn baseTime\n\t}\n\n\tlogger = log.With(logger, \"time\", log.Timestamp(mockTime), \"caller\", log.DefaultCaller)\n\n\tlogger.Log(\"call\", \"first\")\n\tlogger.Log(\"call\", \"second\")\n\n\t// ...\n\n\tlogger.Log(\"call\", \"third\")\n\n\t// Output:\n\t// time=2015-02-03T10:00:01Z caller=example_test.go:93 call=first\n\t// time=2015-02-03T10:00:02Z caller=example_test.go:94 call=second\n\t// time=2015-02-03T10:00:03Z caller=example_test.go:98 call=third\n}\n\nfunc Example_syncWriter() {\n\tw := log.NewSyncWriter(os.Stdout)\n\tlogger := log.NewLogfmtLogger(w)\n\n\ttype Task struct {\n\t\tID int\n\t}\n\n\tvar wg sync.WaitGroup\n\n\tRunTask := func(task Task, logger log.Logger) {\n\t\tlogger.Log(\"taskID\", task.ID, \"event\", \"starting task\")\n\n\t\ttime.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)\n\n\t\tlogger.Log(\"taskID\", task.ID, \"event\", \"task complete\")\n\t\twg.Done()\n\t}\n\n\twg.Add(2)\n\n\tgo RunTask(Task{ID: 1}, logger)\n\tgo RunTask(Task{ID: 2}, logger)\n\n\twg.Wait()\n\n\t// Unordered output:\n\t// taskID=1 event=\"starting task\"\n\t// taskID=2 event=\"starting task\"\n\t// taskID=1 event=\"task complete\"\n\t// taskID=2 event=\"task complete\"\n}\n"
  },
  {
    "path": "log/json_logger.go",
    "content": "package log\n\nimport (\n\t\"io\"\n\n\t\"github.com/go-kit/log\"\n)\n\n// NewJSONLogger returns a Logger that encodes keyvals to the Writer as a\n// single JSON object. Each log event produces no more than one call to\n// w.Write. The passed Writer must be safe for concurrent use by multiple\n// goroutines if the returned Logger will be used concurrently.\nfunc NewJSONLogger(w io.Writer) Logger {\n\treturn log.NewJSONLogger(w)\n}\n"
  },
  {
    "path": "log/level/doc.go",
    "content": "// Package level implements leveled logging on top of Go kit's log package.\n//\n// Deprecated: Use github.com/go-kit/log/level instead.\n//\n// To use the level package, create a logger as per normal in your func main,\n// and wrap it with level.NewFilter.\n//\n//    var logger log.Logger\n//    logger = log.NewLogfmtLogger(os.Stderr)\n//    logger = level.NewFilter(logger, level.AllowInfo()) // <--\n//    logger = log.With(logger, \"ts\", log.DefaultTimestampUTC)\n//\n// Then, at the callsites, use one of the level.Debug, Info, Warn, or Error\n// helper methods to emit leveled log events.\n//\n//    logger.Log(\"foo\", \"bar\") // as normal, no level\n//    level.Debug(logger).Log(\"request_id\", reqID, \"trace_data\", trace.Get())\n//    if value > 100 {\n//        level.Error(logger).Log(\"value\", value)\n//    }\n//\n// NewFilter allows precise control over what happens when a log event is\n// emitted without a level key, or if a squelched level is used. Check the\n// Option functions for details.\npackage level\n"
  },
  {
    "path": "log/level/example_test.go",
    "content": "package level_test\n\nimport (\n\t\"errors\"\n\t\"os\"\n\n\t\"github.com/go-kit/kit/log\"\n\t\"github.com/go-kit/kit/log/level\"\n)\n\nfunc Example_basic() {\n\tlogger := log.NewLogfmtLogger(os.Stdout)\n\tlevel.Debug(logger).Log(\"msg\", \"this message is at the debug level\")\n\tlevel.Info(logger).Log(\"msg\", \"this message is at the info level\")\n\tlevel.Warn(logger).Log(\"msg\", \"this message is at the warn level\")\n\tlevel.Error(logger).Log(\"msg\", \"this message is at the error level\")\n\n\t// Output:\n\t// level=debug msg=\"this message is at the debug level\"\n\t// level=info msg=\"this message is at the info level\"\n\t// level=warn msg=\"this message is at the warn level\"\n\t// level=error msg=\"this message is at the error level\"\n}\n\nfunc Example_filtered() {\n\t// Set up logger with level filter.\n\tlogger := log.NewLogfmtLogger(os.Stdout)\n\tlogger = level.NewFilter(logger, level.AllowInfo())\n\tlogger = log.With(logger, \"caller\", log.DefaultCaller)\n\n\t// Use level helpers to log at different levels.\n\tlevel.Error(logger).Log(\"err\", errors.New(\"bad data\"))\n\tlevel.Info(logger).Log(\"event\", \"data saved\")\n\tlevel.Debug(logger).Log(\"next item\", 17) // filtered\n\n\t// Output:\n\t// level=error caller=example_test.go:32 err=\"bad data\"\n\t// level=info caller=example_test.go:33 event=\"data saved\"\n}\n"
  },
  {
    "path": "log/level/level.go",
    "content": "package level\n\nimport (\n\t\"github.com/go-kit/log\"\n\t\"github.com/go-kit/log/level\"\n)\n\n// Error returns a logger that includes a Key/ErrorValue pair.\nfunc Error(logger log.Logger) log.Logger {\n\treturn level.Error(logger)\n}\n\n// Warn returns a logger that includes a Key/WarnValue pair.\nfunc Warn(logger log.Logger) log.Logger {\n\treturn level.Warn(logger)\n}\n\n// Info returns a logger that includes a Key/InfoValue pair.\nfunc Info(logger log.Logger) log.Logger {\n\treturn level.Info(logger)\n}\n\n// Debug returns a logger that includes a Key/DebugValue pair.\nfunc Debug(logger log.Logger) log.Logger {\n\treturn level.Debug(logger)\n}\n\n// NewFilter wraps next and implements level filtering. See the commentary on\n// the Option functions for a detailed description of how to configure levels.\n// If no options are provided, all leveled log events created with Debug,\n// Info, Warn or Error helper methods are squelched and non-leveled log\n// events are passed to next unmodified.\nfunc NewFilter(next log.Logger, options ...Option) log.Logger {\n\treturn level.NewFilter(next, options...)\n}\n\n// Option sets a parameter for the leveled logger.\ntype Option = level.Option\n\n// AllowAll is an alias for AllowDebug.\nfunc AllowAll() Option {\n\treturn level.AllowAll()\n}\n\n// AllowDebug allows error, warn, info and debug level log events to pass.\nfunc AllowDebug() Option {\n\treturn level.AllowDebug()\n}\n\n// AllowInfo allows error, warn and info level log events to pass.\nfunc AllowInfo() Option {\n\treturn level.AllowInfo()\n}\n\n// AllowWarn allows error and warn level log events to pass.\nfunc AllowWarn() Option {\n\treturn level.AllowWarn()\n}\n\n// AllowError allows only error level log events to pass.\nfunc AllowError() Option {\n\treturn level.AllowError()\n}\n\n// AllowNone allows no leveled log events to pass.\nfunc AllowNone() Option {\n\treturn level.AllowNone()\n}\n\n// ErrNotAllowed sets the error to return from Log when it squelches a log\n// event disallowed by the configured Allow[Level] option. By default,\n// ErrNotAllowed is nil; in this case the log event is squelched with no\n// error.\nfunc ErrNotAllowed(err error) Option {\n\treturn level.ErrNotAllowed(err)\n}\n\n// SquelchNoLevel instructs Log to squelch log events with no level, so that\n// they don't proceed through to the wrapped logger. If SquelchNoLevel is set\n// to true and a log event is squelched in this way, the error value\n// configured with ErrNoLevel is returned to the caller.\nfunc SquelchNoLevel(squelch bool) Option {\n\treturn level.SquelchNoLevel(squelch)\n}\n\n// ErrNoLevel sets the error to return from Log when it squelches a log event\n// with no level. By default, ErrNoLevel is nil; in this case the log event is\n// squelched with no error.\nfunc ErrNoLevel(err error) Option {\n\treturn level.ErrNoLevel(err)\n}\n\n// NewInjector wraps next and returns a logger that adds a Key/level pair to\n// the beginning of log events that don't already contain a level. In effect,\n// this gives a default level to logs without a level.\nfunc NewInjector(next log.Logger, lvl Value) log.Logger {\n\treturn level.NewInjector(next, lvl)\n}\n\n// Value is the interface that each of the canonical level values implement.\n// It contains unexported methods that prevent types from other packages from\n// implementing it and guaranteeing that NewFilter can distinguish the levels\n// defined in this package from all other values.\ntype Value = level.Value\n\n// Key returns the unique key added to log events by the loggers in this\n// package.\nfunc Key() interface{} { return level.Key() }\n\n// ErrorValue returns the unique value added to log events by Error.\nfunc ErrorValue() Value { return level.ErrorValue() }\n\n// WarnValue returns the unique value added to log events by Warn.\nfunc WarnValue() Value { return level.WarnValue() }\n\n// InfoValue returns the unique value added to log events by Info.\nfunc InfoValue() Value { return level.InfoValue() }\n\n// DebugValue returns the unique value added to log events by Debug.\nfunc DebugValue() Value { return level.DebugValue() }\n"
  },
  {
    "path": "log/log.go",
    "content": "package log\n\nimport (\n\t\"github.com/go-kit/log\"\n)\n\n// Logger is the fundamental interface for all log operations. Log creates a\n// log event from keyvals, a variadic sequence of alternating keys and values.\n// Implementations must be safe for concurrent use by multiple goroutines. In\n// particular, any implementation of Logger that appends to keyvals or\n// modifies or retains any of its elements must make a copy first.\ntype Logger = log.Logger\n\n// ErrMissingValue is appended to keyvals slices with odd length to substitute\n// the missing value.\nvar ErrMissingValue = log.ErrMissingValue\n\n// With returns a new contextual logger with keyvals prepended to those passed\n// to calls to Log. If logger is also a contextual logger created by With,\n// WithPrefix, or WithSuffix, keyvals is appended to the existing context.\n//\n// The returned Logger replaces all value elements (odd indexes) containing a\n// Valuer with their generated value for each call to its Log method.\nfunc With(logger Logger, keyvals ...interface{}) Logger {\n\treturn log.With(logger, keyvals...)\n}\n\n// WithPrefix returns a new contextual logger with keyvals prepended to those\n// passed to calls to Log. If logger is also a contextual logger created by\n// With, WithPrefix, or WithSuffix, keyvals is prepended to the existing context.\n//\n// The returned Logger replaces all value elements (odd indexes) containing a\n// Valuer with their generated value for each call to its Log method.\nfunc WithPrefix(logger Logger, keyvals ...interface{}) Logger {\n\treturn log.WithPrefix(logger, keyvals...)\n}\n\n// WithSuffix returns a new contextual logger with keyvals appended to those\n// passed to calls to Log. If logger is also a contextual logger created by\n// With, WithPrefix, or WithSuffix, keyvals is appended to the existing context.\n//\n// The returned Logger replaces all value elements (odd indexes) containing a\n// Valuer with their generated value for each call to its Log method.\nfunc WithSuffix(logger Logger, keyvals ...interface{}) Logger {\n\treturn log.WithSuffix(logger, keyvals...)\n}\n\n// LoggerFunc is an adapter to allow use of ordinary functions as Loggers. If\n// f is a function with the appropriate signature, LoggerFunc(f) is a Logger\n// object that calls f.\ntype LoggerFunc = log.LoggerFunc\n"
  },
  {
    "path": "log/logfmt_logger.go",
    "content": "package log\n\nimport (\n\t\"io\"\n\n\t\"github.com/go-kit/log\"\n)\n\n// NewLogfmtLogger returns a logger that encodes keyvals to the Writer in\n// logfmt format. Each log event produces no more than one call to w.Write.\n// The passed Writer must be safe for concurrent use by multiple goroutines if\n// the returned Logger will be used concurrently.\nfunc NewLogfmtLogger(w io.Writer) Logger {\n\treturn log.NewLogfmtLogger(w)\n}\n"
  },
  {
    "path": "log/logrus/logrus_logger.go",
    "content": "// Package logrus provides an adapter to the\n// go-kit log.Logger interface.\npackage logrus\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/go-kit/log\"\n\t\"github.com/sirupsen/logrus\"\n)\n\ntype Logger struct {\n\tfield logrus.FieldLogger\n\tlevel logrus.Level\n}\n\ntype Option func(*Logger)\n\nvar errMissingValue = errors.New(\"(MISSING)\")\n\n// NewLogger returns a Go kit log.Logger that sends log events to a logrus.Logger.\nfunc NewLogger(logger logrus.FieldLogger, options ...Option) log.Logger {\n\tl := &Logger{\n\t\tfield: logger,\n\t\tlevel: logrus.InfoLevel,\n\t}\n\n\tfor _, optFunc := range options {\n\t\toptFunc(l)\n\t}\n\n\treturn l\n}\n\n// WithLevel configures a logrus logger to log at level for all events.\nfunc WithLevel(level logrus.Level) Option {\n\treturn func(c *Logger) {\n\t\tc.level = level\n\t}\n}\n\nfunc (l Logger) Log(keyvals ...interface{}) error {\n\tfields := logrus.Fields{}\n\tfor i := 0; i < len(keyvals); i += 2 {\n\t\tif i+1 < len(keyvals) {\n\t\t\tfields[fmt.Sprint(keyvals[i])] = keyvals[i+1]\n\t\t} else {\n\t\t\tfields[fmt.Sprint(keyvals[i])] = errMissingValue\n\t\t}\n\t}\n\n\tswitch l.level {\n\tcase logrus.InfoLevel:\n\t\tl.field.WithFields(fields).Info()\n\tcase logrus.ErrorLevel:\n\t\tl.field.WithFields(fields).Error()\n\tcase logrus.DebugLevel:\n\t\tl.field.WithFields(fields).Debug()\n\tcase logrus.WarnLevel:\n\t\tl.field.WithFields(fields).Warn()\n\tcase logrus.TraceLevel:\n\t\tl.field.WithFields(fields).Trace()\n\tdefault:\n\t\tl.field.WithFields(fields).Print()\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "log/logrus/logrus_logger_test.go",
    "content": "package logrus_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"strings\"\n\t\"testing\"\n\n\tlog \"github.com/go-kit/kit/log/logrus\"\n\t\"github.com/sirupsen/logrus\"\n)\n\nfunc TestLogrusLogger(t *testing.T) {\n\tt.Parallel()\n\tbuf := &bytes.Buffer{}\n\tlogrusLogger := logrus.New()\n\tlogrusLogger.Out = buf\n\tlogrusLogger.Formatter = &logrus.TextFormatter{TimestampFormat: \"02-01-2006 15:04:05\", FullTimestamp: true}\n\tlogger := log.NewLogger(logrusLogger)\n\n\tif err := logger.Log(\"hello\", \"world\"); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif want, have := \"hello=world\\n\", strings.Split(buf.String(), \" \")[3]; want != have {\n\t\tt.Errorf(\"want %#v, have %#v\", want, have)\n\t}\n\n\tbuf.Reset()\n\tif err := logger.Log(\"a\", 1, \"err\", errors.New(\"error\")); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif want, have := \"a=1 err=error\", strings.TrimSpace(strings.SplitAfterN(buf.String(), \" \", 4)[3]); want != have {\n\t\tt.Errorf(\"want %#v, have %#v\", want, have)\n\t}\n\n\tbuf.Reset()\n\tif err := logger.Log(\"a\", 1, \"b\"); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif want, have := \"a=1 b=\\\"(MISSING)\\\"\", strings.TrimSpace(strings.SplitAfterN(buf.String(), \" \", 4)[3]); want != have {\n\t\tt.Errorf(\"want %#v, have %#v\", want, have)\n\t}\n\n\tbuf.Reset()\n\tif err := logger.Log(\"my_map\", mymap{0: 0}); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif want, have := \"my_map=special_behavior\", strings.TrimSpace(strings.Split(buf.String(), \" \")[3]); want != have {\n\t\tt.Errorf(\"want %#v, have %#v\", want, have)\n\t}\n}\n\ntype mymap map[int]int\n\nfunc (m mymap) String() string { return \"special_behavior\" }\n\nfunc TestWithLevel(t *testing.T) {\n\ttests := []struct {\n\t\tname          string\n\t\tlevel         logrus.Level\n\t\texpectedLevel logrus.Level\n\t}{\n\t\t{\n\t\t\tname:          \"Test Debug level\",\n\t\t\tlevel:         logrus.DebugLevel,\n\t\t\texpectedLevel: logrus.DebugLevel,\n\t\t},\n\t\t{\n\t\t\tname:          \"Test Error level\",\n\t\t\tlevel:         logrus.ErrorLevel,\n\t\t\texpectedLevel: logrus.ErrorLevel,\n\t\t},\n\t\t{\n\t\t\tname:          \"Test Warn level\",\n\t\t\tlevel:         logrus.WarnLevel,\n\t\t\texpectedLevel: logrus.WarnLevel,\n\t\t},\n\t\t{\n\t\t\tname:          \"Test Info level\",\n\t\t\tlevel:         logrus.InfoLevel,\n\t\t\texpectedLevel: logrus.InfoLevel,\n\t\t},\n\t\t{\n\t\t\tname:          \"Test Trace level\",\n\t\t\tlevel:         logrus.TraceLevel,\n\t\t\texpectedLevel: logrus.TraceLevel,\n\t\t},\n\t\t{\n\t\t\tname:          \"Test not existing level\",\n\t\t\tlevel:         999,\n\t\t\texpectedLevel: logrus.InfoLevel,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tbuf := &bytes.Buffer{}\n\t\tlogrusLogger := logrus.New()\n\t\tlogrusLogger.Out = buf\n\t\tlogrusLogger.Level = tt.level\n\t\tlogrusLogger.Formatter = &logrus.JSONFormatter{}\n\t\tlogger := log.NewLogger(logrusLogger, log.WithLevel(tt.level))\n\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif err := logger.Log(); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\tl := map[string]interface{}{}\n\t\t\tif err := json.Unmarshal(buf.Bytes(), &l); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\tif v, ok := l[\"level\"].(string); !ok || v != tt.expectedLevel.String() {\n\t\t\t\tt.Fatalf(\"Logging levels doesn't match. Expected: %s, got: %s\", tt.level, v)\n\t\t\t}\n\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "log/nop_logger.go",
    "content": "package log\n\nimport \"github.com/go-kit/log\"\n\n// NewNopLogger returns a logger that doesn't do anything.\nfunc NewNopLogger() Logger {\n\treturn log.NewNopLogger()\n}\n"
  },
  {
    "path": "log/stdlib.go",
    "content": "package log\n\nimport (\n\t\"io\"\n\n\t\"github.com/go-kit/log\"\n)\n\n// StdlibWriter implements io.Writer by invoking the stdlib log.Print. It's\n// designed to be passed to a Go kit logger as the writer, for cases where\n// it's necessary to redirect all Go kit log output to the stdlib logger.\n//\n// If you have any choice in the matter, you shouldn't use this. Prefer to\n// redirect the stdlib log to the Go kit logger via NewStdlibAdapter.\ntype StdlibWriter = log.StdlibWriter\n\n// StdlibAdapter wraps a Logger and allows it to be passed to the stdlib\n// logger's SetOutput. It will extract date/timestamps, filenames, and\n// messages, and place them under relevant keys.\ntype StdlibAdapter = log.StdlibAdapter\n\n// StdlibAdapterOption sets a parameter for the StdlibAdapter.\ntype StdlibAdapterOption = log.StdlibAdapterOption\n\n// TimestampKey sets the key for the timestamp field. By default, it's \"ts\".\nfunc TimestampKey(key string) StdlibAdapterOption {\n\treturn log.TimestampKey(key)\n}\n\n// FileKey sets the key for the file and line field. By default, it's \"caller\".\nfunc FileKey(key string) StdlibAdapterOption {\n\treturn log.FileKey(key)\n}\n\n// MessageKey sets the key for the actual log message. By default, it's \"msg\".\nfunc MessageKey(key string) StdlibAdapterOption {\n\treturn log.MessageKey(key)\n}\n\n// Prefix configures the adapter to parse a prefix from stdlib log events. If\n// you provide a non-empty prefix to the stdlib logger, then your should provide\n// that same prefix to the adapter via this option.\n//\n// By default, the prefix isn't included in the msg key. Set joinPrefixToMsg to\n// true if you want to include the parsed prefix in the msg.\nfunc Prefix(prefix string, joinPrefixToMsg bool) StdlibAdapterOption {\n\treturn log.Prefix(prefix, joinPrefixToMsg)\n}\n\n// NewStdlibAdapter returns a new StdlibAdapter wrapper around the passed\n// logger. It's designed to be passed to log.SetOutput.\nfunc NewStdlibAdapter(logger Logger, options ...StdlibAdapterOption) io.Writer {\n\treturn log.NewStdlibAdapter(logger, options...)\n}\n"
  },
  {
    "path": "log/sync.go",
    "content": "package log\n\nimport (\n\t\"io\"\n\n\t\"github.com/go-kit/log\"\n)\n\n// SwapLogger wraps another logger that may be safely replaced while other\n// goroutines use the SwapLogger concurrently. The zero value for a SwapLogger\n// will discard all log events without error.\n//\n// SwapLogger serves well as a package global logger that can be changed by\n// importers.\ntype SwapLogger = log.SwapLogger\n\n// NewSyncWriter returns a new writer that is safe for concurrent use by\n// multiple goroutines. Writes to the returned writer are passed on to w. If\n// another write is already in progress, the calling goroutine blocks until\n// the writer is available.\n//\n// If w implements the following interface, so does the returned writer.\n//\n//    interface {\n//        Fd() uintptr\n//    }\nfunc NewSyncWriter(w io.Writer) io.Writer {\n\treturn log.NewSyncWriter(w)\n}\n\n// NewSyncLogger returns a logger that synchronizes concurrent use of the\n// wrapped logger. When multiple goroutines use the SyncLogger concurrently\n// only one goroutine will be allowed to log to the wrapped logger at a time.\n// The other goroutines will block until the logger is available.\nfunc NewSyncLogger(logger Logger) Logger {\n\treturn log.NewSyncLogger(logger)\n}\n"
  },
  {
    "path": "log/syslog/example_test.go",
    "content": "// +build !windows\n// +build !plan9\n// +build !nacl\n\npackage syslog_test\n\nimport (\n\t\"fmt\"\n\n\tgosyslog \"log/syslog\"\n\n\t\"github.com/go-kit/kit/log\"\n\t\"github.com/go-kit/kit/log/level\"\n\t\"github.com/go-kit/kit/log/syslog\"\n)\n\nfunc ExampleNewSyslogLogger_defaultPrioritySelector() {\n\t// Normal syslog writer\n\tw, err := gosyslog.New(gosyslog.LOG_INFO, \"experiment\")\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\n\t// syslog logger with logfmt formatting\n\tlogger := syslog.NewSyslogLogger(w, log.NewLogfmtLogger)\n\tlogger.Log(\"msg\", \"info because of default\")\n\tlogger.Log(level.Key(), level.DebugValue(), \"msg\", \"debug because of explicit level\")\n}\n"
  },
  {
    "path": "log/syslog/syslog.go",
    "content": "//go:build !windows && !plan9 && !nacl\n// +build !windows,!plan9,!nacl\n\n// Deprecated: Use github.com/go-kit/log/syslog instead.\npackage syslog\n\nimport (\n\t\"io\"\n\n\t\"github.com/go-kit/log\"\n\t\"github.com/go-kit/log/syslog\"\n)\n\n// SyslogWriter is an interface wrapping stdlib syslog Writer.\ntype SyslogWriter = syslog.SyslogWriter\n\n// NewSyslogLogger returns a new Logger which writes to syslog in syslog format.\n// The body of the log message is the formatted output from the Logger returned\n// by newLogger.\nfunc NewSyslogLogger(w SyslogWriter, newLogger func(io.Writer) log.Logger, options ...Option) log.Logger {\n\treturn syslog.NewSyslogLogger(w, newLogger, options...)\n}\n\n// Option sets a parameter for syslog loggers.\ntype Option = syslog.Option\n\n// PrioritySelector inspects the list of keyvals and selects a syslog priority.\ntype PrioritySelector = syslog.PrioritySelector\n\n// PrioritySelectorOption sets priority selector function to choose syslog\n// priority.\nfunc PrioritySelectorOption(selector PrioritySelector) Option {\n\treturn syslog.PrioritySelectorOption(selector)\n}\n"
  },
  {
    "path": "log/term/colorlogger.go",
    "content": "package term\n\nimport (\n\t\"io\"\n\n\t\"github.com/go-kit/log\"\n\t\"github.com/go-kit/log/term\"\n)\n\n// Color represents an ANSI color. The zero value is Default.\ntype Color = term.Color\n\n// ANSI colors.\nconst (\n\tDefault = term.Default\n\n\tBlack       = term.Black\n\tDarkRed     = term.DarkRed\n\tDarkGreen   = term.DarkGreen\n\tBrown       = term.Brown\n\tDarkBlue    = term.DarkBlue\n\tDarkMagenta = term.DarkMagenta\n\tDarkCyan    = term.DarkCyan\n\tGray        = term.Gray\n\n\tDarkGray = term.DarkGray\n\tRed      = term.Red\n\tGreen    = term.Green\n\tYellow   = term.Yellow\n\tBlue     = term.Blue\n\tMagenta  = term.Magenta\n\tCyan     = term.Cyan\n\tWhite    = term.White\n)\n\n// FgBgColor represents a foreground and background color.\ntype FgBgColor = term.FgBgColor\n\n// NewColorLogger returns a Logger which writes colored logs to w. ANSI color\n// codes for the colors returned by color are added to the formatted output\n// from the Logger returned by newLogger and the combined result written to w.\nfunc NewColorLogger(w io.Writer, newLogger func(io.Writer) log.Logger, color func(keyvals ...interface{}) FgBgColor) log.Logger {\n\treturn term.NewColorLogger(w, newLogger, color)\n}\n"
  },
  {
    "path": "log/term/colorwriter.go",
    "content": "package term\n\nimport (\n\t\"io\"\n\n\t\"github.com/go-kit/log/term\"\n)\n\n// NewColorWriter returns an io.Writer that writes to w and provides cross\n// platform support for ANSI color codes. If w is not a terminal it is\n// returned unmodified.\nfunc NewColorWriter(w io.Writer) io.Writer {\n\treturn term.NewColorWriter(w)\n}\n"
  },
  {
    "path": "log/term/example_test.go",
    "content": "package term_test\n\nimport (\n\t\"errors\"\n\t\"os\"\n\n\t\"github.com/go-kit/kit/log\"\n\t\"github.com/go-kit/kit/log/term\"\n)\n\nfunc ExampleNewLogger_redErrors() {\n\t// Color errors red\n\tcolorFn := func(keyvals ...interface{}) term.FgBgColor {\n\t\tfor i := 1; i < len(keyvals); i += 2 {\n\t\t\tif _, ok := keyvals[i].(error); ok {\n\t\t\t\treturn term.FgBgColor{Fg: term.White, Bg: term.Red}\n\t\t\t}\n\t\t}\n\t\treturn term.FgBgColor{}\n\t}\n\n\tlogger := term.NewLogger(os.Stdout, log.NewLogfmtLogger, colorFn)\n\n\tlogger.Log(\"msg\", \"default color\", \"err\", nil)\n\tlogger.Log(\"msg\", \"colored because of error\", \"err\", errors.New(\"coloring error\"))\n}\n\nfunc ExampleNewLogger_levelColors() {\n\t// Color by level value\n\tcolorFn := func(keyvals ...interface{}) term.FgBgColor {\n\t\tfor i := 0; i < len(keyvals)-1; i += 2 {\n\t\t\tif keyvals[i] != \"level\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tswitch keyvals[i+1] {\n\t\t\tcase \"debug\":\n\t\t\t\treturn term.FgBgColor{Fg: term.DarkGray}\n\t\t\tcase \"info\":\n\t\t\t\treturn term.FgBgColor{Fg: term.Gray}\n\t\t\tcase \"warn\":\n\t\t\t\treturn term.FgBgColor{Fg: term.Yellow}\n\t\t\tcase \"error\":\n\t\t\t\treturn term.FgBgColor{Fg: term.Red}\n\t\t\tcase \"crit\":\n\t\t\t\treturn term.FgBgColor{Fg: term.Gray, Bg: term.DarkRed}\n\t\t\tdefault:\n\t\t\t\treturn term.FgBgColor{}\n\t\t\t}\n\t\t}\n\t\treturn term.FgBgColor{}\n\t}\n\n\tlogger := term.NewLogger(os.Stdout, log.NewJSONLogger, colorFn)\n\n\tlogger.Log(\"level\", \"warn\", \"msg\", \"yellow\")\n\tlogger.Log(\"level\", \"debug\", \"msg\", \"dark gray\")\n}\n"
  },
  {
    "path": "log/term/term.go",
    "content": "// Package term provides tools for logging to a terminal.\n//\n// Deprecated: Use github.com/go-kit/log/term instead.\npackage term\n\nimport (\n\t\"io\"\n\n\t\"github.com/go-kit/log\"\n\t\"github.com/go-kit/log/term\"\n)\n\n// NewLogger returns a Logger that takes advantage of terminal features if\n// possible. Log events are formatted by the Logger returned by newLogger. If\n// w is a terminal each log event is colored according to the color function.\nfunc NewLogger(w io.Writer, newLogger func(io.Writer) log.Logger, color func(keyvals ...interface{}) FgBgColor) log.Logger {\n\treturn term.NewLogger(w, newLogger, color)\n}\n\n// IsTerminal returns true if w writes to a terminal.\nfunc IsTerminal(w io.Writer) bool {\n\treturn term.IsTerminal(w)\n}\n"
  },
  {
    "path": "log/value.go",
    "content": "package log\n\nimport (\n\t\"time\"\n\n\t\"github.com/go-kit/log\"\n)\n\n// A Valuer generates a log value. When passed to With, WithPrefix, or\n// WithSuffix in a value element (odd indexes), it represents a dynamic\n// value which is re-evaluated with each log event.\ntype Valuer = log.Valuer\n\n// Timestamp returns a timestamp Valuer. It invokes the t function to get the\n// time; unless you are doing something tricky, pass time.Now.\n//\n// Most users will want to use DefaultTimestamp or DefaultTimestampUTC, which\n// are TimestampFormats that use the RFC3339Nano format.\nfunc Timestamp(t func() time.Time) Valuer {\n\treturn log.Timestamp(t)\n}\n\n// TimestampFormat returns a timestamp Valuer with a custom time format. It\n// invokes the t function to get the time to format; unless you are doing\n// something tricky, pass time.Now. The layout string is passed to\n// Time.Format.\n//\n// Most users will want to use DefaultTimestamp or DefaultTimestampUTC, which\n// are TimestampFormats that use the RFC3339Nano format.\nfunc TimestampFormat(t func() time.Time, layout string) Valuer {\n\treturn log.TimestampFormat(t, layout)\n}\n\n// Caller returns a Valuer that returns a file and line from a specified depth\n// in the callstack. Users will probably want to use DefaultCaller.\nfunc Caller(depth int) Valuer {\n\treturn log.Caller(depth)\n}\n\nvar (\n\t// DefaultTimestamp is a Valuer that returns the current wallclock time,\n\t// respecting time zones, when bound.\n\tDefaultTimestamp = log.DefaultTimestamp\n\n\t// DefaultTimestampUTC is a Valuer that returns the current time in UTC\n\t// when bound.\n\tDefaultTimestampUTC = log.DefaultTimestampUTC\n\n\t// DefaultCaller is a Valuer that returns the file and line where the Log\n\t// method was invoked. It can only be used with log.With.\n\tDefaultCaller = log.DefaultCaller\n)\n"
  },
  {
    "path": "log/zap/zap_sugar_logger.go",
    "content": "package zap\n\nimport (\n\t\"github.com/go-kit/log\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\ntype zapSugarLogger func(msg string, keysAndValues ...interface{})\n\nfunc (l zapSugarLogger) Log(kv ...interface{}) error {\n\tl(\"\", kv...)\n\treturn nil\n}\n\n// NewZapSugarLogger returns a Go kit log.Logger that sends\n// log events to a zap.Logger.\nfunc NewZapSugarLogger(logger *zap.Logger, level zapcore.Level) log.Logger {\n\tsugarLogger := logger.WithOptions(zap.AddCallerSkip(2)).Sugar()\n\tvar sugar zapSugarLogger\n\tswitch level {\n\tcase zapcore.DebugLevel:\n\t\tsugar = sugarLogger.Debugw\n\tcase zapcore.InfoLevel:\n\t\tsugar = sugarLogger.Infow\n\tcase zapcore.WarnLevel:\n\t\tsugar = sugarLogger.Warnw\n\tcase zapcore.ErrorLevel:\n\t\tsugar = sugarLogger.Errorw\n\tcase zapcore.DPanicLevel:\n\t\tsugar = sugarLogger.DPanicw\n\tcase zapcore.PanicLevel:\n\t\tsugar = sugarLogger.Panicw\n\tcase zapcore.FatalLevel:\n\t\tsugar = sugarLogger.Fatalw\n\tdefault:\n\t\tsugar = sugarLogger.Infow\n\t}\n\treturn sugar\n}\n"
  },
  {
    "path": "log/zap/zap_sugar_logger_test.go",
    "content": "package zap_test\n\nimport (\n\t\"encoding/json\"\n\tkitzap \"github.com/go-kit/kit/log/zap\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestZapSugarLogger(t *testing.T) {\n\t// logger config\n\tencoderConfig := zap.NewDevelopmentEncoderConfig()\n\tencoder := zapcore.NewJSONEncoder(encoderConfig)\n\tlevelKey := encoderConfig.LevelKey\n\t// basic test cases\n\ttype testCase struct {\n\t\tlevel zapcore.Level\n\t\tkvs   []interface{}\n\t\twant  map[string]string\n\t}\n\ttestCases := []testCase{\n\t\t{level: zapcore.DebugLevel, kvs: []interface{}{\"key1\", \"value1\"},\n\t\t\twant: map[string]string{levelKey: \"DEBUG\", \"key1\": \"value1\"}},\n\n\t\t{level: zapcore.InfoLevel, kvs: []interface{}{\"key2\", \"value2\"},\n\t\t\twant: map[string]string{levelKey: \"INFO\", \"key2\": \"value2\"}},\n\n\t\t{level: zapcore.WarnLevel, kvs: []interface{}{\"key3\", \"value3\"},\n\t\t\twant: map[string]string{levelKey: \"WARN\", \"key3\": \"value3\"}},\n\n\t\t{level: zapcore.ErrorLevel, kvs: []interface{}{\"key4\", \"value4\"},\n\t\t\twant: map[string]string{levelKey: \"ERROR\", \"key4\": \"value4\"}},\n\n\t\t{level: zapcore.DPanicLevel, kvs: []interface{}{\"key5\", \"value5\"},\n\t\t\twant: map[string]string{levelKey: \"DPANIC\", \"key5\": \"value5\"}},\n\n\t\t{level: zapcore.PanicLevel, kvs: []interface{}{\"key6\", \"value6\"},\n\t\t\twant: map[string]string{levelKey: \"PANIC\", \"key6\": \"value6\"}},\n\t}\n\t// test\n\tfor _, testCase := range testCases {\n\t\tt.Run(testCase.level.String(), func(t *testing.T) {\n\t\t\t// make logger\n\t\t\twriter := &tbWriter{tb: t}\n\t\t\tlogger := zap.New(\n\t\t\t\tzapcore.NewCore(encoder, zapcore.AddSync(writer), zap.DebugLevel),\n\t\t\t\tzap.Development())\n\t\t\t// check panic\n\t\t\tshouldPanic := testCase.level >= zapcore.DPanicLevel\n\t\t\tkitLogger := kitzap.NewZapSugarLogger(logger, testCase.level)\n\t\t\tdefer func() {\n\t\t\t\tisPanic := recover() != nil\n\t\t\t\tif shouldPanic != isPanic {\n\t\t\t\t\tt.Errorf(\"test level %v should panic(%v), but %v\", testCase.level, shouldPanic, isPanic)\n\t\t\t\t}\n\t\t\t\t// check log kvs\n\t\t\t\tlogMap := make(map[string]string)\n\t\t\t\terr := json.Unmarshal([]byte(writer.sb.String()), &logMap)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Errorf(\"unmarshal error: %v\", err)\n\t\t\t\t} else {\n\t\t\t\t\tfor k, v := range testCase.want {\n\t\t\t\t\t\tvv, ok := logMap[k]\n\t\t\t\t\t\tif !ok || v != vv {\n\t\t\t\t\t\t\tt.Error(\"error log\")\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}()\n\t\t\tkitLogger.Log(testCase.kvs...)\n\t\t})\n\t}\n}\n\ntype tbWriter struct {\n\ttb testing.TB\n\tsb strings.Builder\n}\n\nfunc (w *tbWriter) Write(b []byte) (n int, err error) {\n\tw.tb.Logf(string(b))\n\tw.sb.Write(b)\n\treturn len(b), nil\n}\n"
  },
  {
    "path": "metrics/README.md",
    "content": "# package metrics\n\n`package metrics` provides a set of uniform interfaces for service instrumentation.\nIt has\n [counters](http://prometheus.io/docs/concepts/metric_types/#counter),\n [gauges](http://prometheus.io/docs/concepts/metric_types/#gauge), and\n [histograms](http://prometheus.io/docs/concepts/metric_types/#histogram),\nand provides adapters to popular metrics packages, like\n [expvar](https://golang.org/pkg/expvar),\n [StatsD](https://github.com/etsy/statsd), and\n [Prometheus](https://prometheus.io).\n\n## Rationale\n\nCode instrumentation is absolutely essential to achieve\n [observability](https://speakerdeck.com/mattheath/observability-in-micro-service-architectures)\n into a distributed system.\nMetrics and instrumentation tools have coalesced around a few well-defined idioms.\n`package metrics` provides a common, minimal interface those idioms for service authors.\n\n## Usage\n\nA simple counter, exported via expvar.\n\n```go\nimport (\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/expvar\"\n)\n\nfunc main() {\n\tvar myCount metrics.Counter\n\tmyCount = expvar.NewCounter(\"my_count\")\n\tmyCount.Add(1)\n}\n```\n\nA histogram for request duration,\n exported via a Prometheus summary with dynamically-computed quantiles.\n\n```go\nimport (\n\t\"time\"\n\n\tstdprometheus \"github.com/prometheus/client_golang/prometheus\"\n\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/prometheus\"\n)\n\nfunc main() {\n\tvar dur metrics.Histogram = prometheus.NewSummaryFrom(stdprometheus.SummaryOpts{\n\t\tNamespace: \"myservice\",\n\t\tSubsystem: \"api\",\n\t\tName:     \"request_duration_seconds\",\n\t\tHelp:     \"Total time spent serving requests.\",\n\t}, []string{})\n\t// ...\n}\n\nfunc handleRequest(dur metrics.Histogram) {\n\tdefer func(begin time.Time) { dur.Observe(time.Since(begin).Seconds()) }(time.Now())\n\t// handle request\n}\n```\n\nA gauge for the number of goroutines currently running, exported via StatsD.\n\n```go\nimport (\n\t\"context\"\n\t\"net\"\n\t\"os\"\n\t\"runtime\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/statsd\"\n)\n\nfunc main() {\n\tstatsd := statsd.New(\"foo_svc.\", log.NewNopLogger())\n\treport := time.NewTicker(5 * time.Second)\n\tdefer report.Stop()\n\tgo statsd.SendLoop(context.Background(), report.C, \"tcp\", \"statsd.internal:8125\")\n\tgoroutines := statsd.NewGauge(\"goroutine_count\")\n\tgo exportGoroutines(goroutines)\n\t// ...\n}\n\nfunc exportGoroutines(g metrics.Gauge) {\n\tfor range time.Tick(time.Second) {\n\t\tg.Set(float64(runtime.NumGoroutine()))\n\t}\n}\n```\n\nFor more information, see [the package documentation](https://godoc.org/github.com/go-kit/kit/metrics).\n"
  },
  {
    "path": "metrics/cloudwatch/cloudwatch.go",
    "content": "package cloudwatch\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/aws/aws-sdk-go/aws\"\n\t\"github.com/aws/aws-sdk-go/service/cloudwatch\"\n\t\"github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface\"\n\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/generic\"\n\t\"github.com/go-kit/kit/metrics/internal/lv\"\n\t\"github.com/go-kit/log\"\n)\n\nconst (\n\tmaxConcurrentRequests = 20\n\tmaxValuesInABatch     = 150\n)\n\n// CloudWatch receives metrics observations and forwards them to CloudWatch.\n// Create a CloudWatch object, use it to create metrics, and pass those metrics as\n// dependencies to the components that will use them.\n//\n// To regularly report metrics to CloudWatch, use the WriteLoop helper method.\ntype CloudWatch struct {\n\tmtx                   sync.RWMutex\n\tsem                   chan struct{}\n\tnamespace             string\n\tsvc                   cloudwatchiface.CloudWatchAPI\n\tcounters              *lv.Space\n\tgauges                *lv.Space\n\thistograms            *lv.Space\n\tpercentiles           []float64 // percentiles to track\n\tlogger                log.Logger\n\tnumConcurrentRequests int\n}\n\n// Option is a function adapter to change config of the CloudWatch struct\ntype Option func(*CloudWatch)\n\n// WithLogger sets the Logger that will receive error messages generated\n// during the WriteLoop. By default, fmt logger is used.\nfunc WithLogger(logger log.Logger) Option {\n\treturn func(c *CloudWatch) {\n\t\tc.logger = logger\n\t}\n}\n\n// WithPercentiles registers the percentiles to track, overriding the\n// existing/default values.\n// Reason is that Cloudwatch makes you pay per metric, so you can save half the money\n// by only using 2 metrics instead of the default 4.\nfunc WithPercentiles(percentiles ...float64) Option {\n\treturn func(c *CloudWatch) {\n\t\tc.percentiles = make([]float64, 0, len(percentiles))\n\t\tfor _, p := range percentiles {\n\t\t\tif p < 0 || p > 1 {\n\t\t\t\tcontinue // illegal entry; ignore\n\t\t\t}\n\t\t\tc.percentiles = append(c.percentiles, p)\n\t\t}\n\t}\n}\n\n// WithConcurrentRequests sets the upper limit on how many\n// cloudwatch.PutMetricDataRequest may be under way at any\n// given time. If n is greater than 20, 20 is used. By default,\n// the max is set at 10 concurrent requests.\nfunc WithConcurrentRequests(n int) Option {\n\treturn func(c *CloudWatch) {\n\t\tif n > maxConcurrentRequests {\n\t\t\tn = maxConcurrentRequests\n\t\t}\n\t\tc.numConcurrentRequests = n\n\t}\n}\n\n// New returns a CloudWatch object that may be used to create metrics.\n// Namespace is applied to all created metrics and maps to the CloudWatch namespace.\n// Callers must ensure that regular calls to Send are performed, either\n// manually or with one of the helper methods.\nfunc New(namespace string, svc cloudwatchiface.CloudWatchAPI, options ...Option) *CloudWatch {\n\tcw := &CloudWatch{\n\t\tsem:                   nil, // set below\n\t\tnamespace:             namespace,\n\t\tsvc:                   svc,\n\t\tcounters:              lv.NewSpace(),\n\t\tgauges:                lv.NewSpace(),\n\t\thistograms:            lv.NewSpace(),\n\t\tnumConcurrentRequests: 10,\n\t\tlogger:                log.NewLogfmtLogger(os.Stderr),\n\t\tpercentiles:           []float64{0.50, 0.90, 0.95, 0.99},\n\t}\n\n\tfor _, opt := range options {\n\t\topt(cw)\n\t}\n\n\tcw.sem = make(chan struct{}, cw.numConcurrentRequests)\n\n\treturn cw\n}\n\n// NewCounter returns a counter. Observations are aggregated and emitted once\n// per write invocation.\nfunc (cw *CloudWatch) NewCounter(name string) metrics.Counter {\n\treturn &Counter{\n\t\tname: name,\n\t\tobs:  cw.counters.Observe,\n\t}\n}\n\n// NewGauge returns an gauge.\nfunc (cw *CloudWatch) NewGauge(name string) metrics.Gauge {\n\treturn &Gauge{\n\t\tname: name,\n\t\tobs:  cw.gauges.Observe,\n\t\tadd:  cw.gauges.Add,\n\t}\n}\n\n// NewHistogram returns a histogram.\nfunc (cw *CloudWatch) NewHistogram(name string) metrics.Histogram {\n\treturn &Histogram{\n\t\tname: name,\n\t\tobs:  cw.histograms.Observe,\n\t}\n}\n\n// WriteLoop is a helper method that invokes Send every time the passed\n// channel fires. This method blocks until ctx is canceled, so clients\n// probably want to run it in its own goroutine. For typical usage, create a\n// time.Ticker and pass its C channel to this method.\nfunc (cw *CloudWatch) WriteLoop(ctx context.Context, c <-chan time.Time) {\n\tfor {\n\t\tselect {\n\t\tcase <-c:\n\t\t\tif err := cw.Send(); err != nil {\n\t\t\t\tcw.logger.Log(\"during\", \"Send\", \"err\", err)\n\t\t\t}\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// Send will fire an API request to CloudWatch with the latest stats for\n// all metrics. It is preferred that the WriteLoop method is used.\nfunc (cw *CloudWatch) Send() error {\n\tcw.mtx.RLock()\n\tdefer cw.mtx.RUnlock()\n\tnow := time.Now()\n\n\tvar datums []*cloudwatch.MetricDatum\n\n\tcw.counters.Reset().Walk(func(name string, lvs lv.LabelValues, values []float64) bool {\n\t\tvalue := sum(values)\n\t\tdatums = append(datums, &cloudwatch.MetricDatum{\n\t\t\tMetricName: aws.String(name),\n\t\t\tDimensions: makeDimensions(lvs...),\n\t\t\tValue:      aws.Float64(value),\n\t\t\tTimestamp:  aws.Time(now),\n\t\t})\n\t\treturn true\n\t})\n\n\tcw.gauges.Reset().Walk(func(name string, lvs lv.LabelValues, values []float64) bool {\n\t\tif len(values) == 0 {\n\t\t\treturn true\n\t\t}\n\n\t\tdatum := &cloudwatch.MetricDatum{\n\t\t\tMetricName: aws.String(name),\n\t\t\tDimensions: makeDimensions(lvs...),\n\t\t\tTimestamp:  aws.Time(now),\n\t\t}\n\n\t\t// CloudWatch Put Metrics API (https://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_MetricDatum.html)\n\t\t// expects batch of unique values including the array of corresponding counts\n\t\tvaluesCounter := make(map[float64]int)\n\t\tfor _, v := range values {\n\t\t\tvaluesCounter[v]++\n\t\t}\n\n\t\tfor value, count := range valuesCounter {\n\t\t\tif len(datum.Values) == maxValuesInABatch {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tdatum.Values = append(datum.Values, aws.Float64(value))\n\t\t\tdatum.Counts = append(datum.Counts, aws.Float64(float64(count)))\n\t\t}\n\n\t\tdatums = append(datums, datum)\n\t\treturn true\n\t})\n\n\t// format a [0,1]-float value to a percentile value, with minimum nr of decimals\n\t// 0.90 -> \"90\"\n\t// 0.95 -> \"95\"\n\t// 0.999 -> \"99.9\"\n\tformatPerc := func(p float64) string {\n\t\treturn strconv.FormatFloat(p*100, 'f', -1, 64)\n\t}\n\n\tcw.histograms.Reset().Walk(func(name string, lvs lv.LabelValues, values []float64) bool {\n\t\thistogram := generic.NewHistogram(name, 50)\n\n\t\tfor _, v := range values {\n\t\t\thistogram.Observe(v)\n\t\t}\n\n\t\tfor _, perc := range cw.percentiles {\n\t\t\tvalue := histogram.Quantile(perc)\n\t\t\tdatums = append(datums, &cloudwatch.MetricDatum{\n\t\t\t\tMetricName: aws.String(fmt.Sprintf(\"%s_%s\", name, formatPerc(perc))),\n\t\t\t\tDimensions: makeDimensions(lvs...),\n\t\t\t\tValue:      aws.Float64(value),\n\t\t\t\tTimestamp:  aws.Time(now),\n\t\t\t})\n\t\t}\n\t\treturn true\n\t})\n\n\tvar batches [][]*cloudwatch.MetricDatum\n\tfor len(datums) > 0 {\n\t\tvar batch []*cloudwatch.MetricDatum\n\t\tlim := min(len(datums), maxConcurrentRequests)\n\t\tbatch, datums = datums[:lim], datums[lim:]\n\t\tbatches = append(batches, batch)\n\t}\n\n\tvar errors = make(chan error, len(batches))\n\tfor _, batch := range batches {\n\t\tgo func(batch []*cloudwatch.MetricDatum) {\n\t\t\tcw.sem <- struct{}{}\n\t\t\tdefer func() {\n\t\t\t\t<-cw.sem\n\t\t\t}()\n\t\t\t_, err := cw.svc.PutMetricData(&cloudwatch.PutMetricDataInput{\n\t\t\t\tNamespace:  aws.String(cw.namespace),\n\t\t\t\tMetricData: batch,\n\t\t\t})\n\t\t\terrors <- err\n\t\t}(batch)\n\t}\n\tvar firstErr error\n\tfor i := 0; i < cap(errors); i++ {\n\t\tif err := <-errors; err != nil && firstErr == nil {\n\t\t\tfirstErr = err\n\t\t}\n\t}\n\n\treturn firstErr\n}\n\nfunc sum(a []float64) float64 {\n\tvar v float64\n\tfor _, f := range a {\n\t\tv += f\n\t}\n\treturn v\n}\n\nfunc min(a, b int) int {\n\tif a < b {\n\t\treturn a\n\t}\n\treturn b\n}\n\ntype observeFunc func(name string, lvs lv.LabelValues, value float64)\n\n// Counter is a counter. Observations are forwarded to a node\n// object, and aggregated (summed) per timeseries.\ntype Counter struct {\n\tname string\n\tlvs  lv.LabelValues\n\tobs  observeFunc\n}\n\n// With implements metrics.Counter.\nfunc (c *Counter) With(labelValues ...string) metrics.Counter {\n\treturn &Counter{\n\t\tname: c.name,\n\t\tlvs:  c.lvs.With(labelValues...),\n\t\tobs:  c.obs,\n\t}\n}\n\n// Add implements metrics.Counter.\nfunc (c *Counter) Add(delta float64) {\n\tc.obs(c.name, c.lvs, delta)\n}\n\n// Gauge is a gauge. Observations are forwarded to a node\n// object, and aggregated (the last observation selected) per timeseries.\ntype Gauge struct {\n\tname string\n\tlvs  lv.LabelValues\n\tobs  observeFunc\n\tadd  observeFunc\n}\n\n// With implements metrics.Gauge.\nfunc (g *Gauge) With(labelValues ...string) metrics.Gauge {\n\treturn &Gauge{\n\t\tname: g.name,\n\t\tlvs:  g.lvs.With(labelValues...),\n\t\tobs:  g.obs,\n\t\tadd:  g.add,\n\t}\n}\n\n// Set implements metrics.Gauge.\nfunc (g *Gauge) Set(value float64) {\n\tg.obs(g.name, g.lvs, value)\n}\n\n// Add implements metrics.Gauge.\nfunc (g *Gauge) Add(delta float64) {\n\tg.add(g.name, g.lvs, delta)\n}\n\n// Histogram is an Influx histrogram. Observations are aggregated into a\n// generic.Histogram and emitted as per-quantile gauges to the Influx server.\ntype Histogram struct {\n\tname string\n\tlvs  lv.LabelValues\n\tobs  observeFunc\n}\n\n// With implements metrics.Histogram.\nfunc (h *Histogram) With(labelValues ...string) metrics.Histogram {\n\treturn &Histogram{\n\t\tname: h.name,\n\t\tlvs:  h.lvs.With(labelValues...),\n\t\tobs:  h.obs,\n\t}\n}\n\n// Observe implements metrics.Histogram.\nfunc (h *Histogram) Observe(value float64) {\n\th.obs(h.name, h.lvs, value)\n}\n\nfunc makeDimensions(labelValues ...string) []*cloudwatch.Dimension {\n\tdimensions := make([]*cloudwatch.Dimension, len(labelValues)/2)\n\tfor i, j := 0, 0; i < len(labelValues); i, j = i+2, j+1 {\n\t\tdimensions[j] = &cloudwatch.Dimension{\n\t\t\tName:  aws.String(labelValues[i]),\n\t\t\tValue: aws.String(labelValues[i+1]),\n\t\t}\n\t}\n\treturn dimensions\n}\n"
  },
  {
    "path": "metrics/cloudwatch/cloudwatch_test.go",
    "content": "package cloudwatch\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/aws/aws-sdk-go/service/cloudwatch\"\n\t\"github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface\"\n\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/teststat\"\n\t\"github.com/go-kit/log\"\n)\n\nconst metricNameToGenerateError = \"metric_name_used_to_throw_an_error\"\n\nvar errTest = errors.New(\"test error\")\n\ntype mockCloudWatch struct {\n\tcloudwatchiface.CloudWatchAPI\n\tmtx                sync.RWMutex\n\tvaluesReceived     map[string][]float64\n\tdimensionsReceived map[string][]*cloudwatch.Dimension\n}\n\nfunc newMockCloudWatch() *mockCloudWatch {\n\treturn &mockCloudWatch{\n\t\tvaluesReceived:     map[string][]float64{},\n\t\tdimensionsReceived: map[string][]*cloudwatch.Dimension{},\n\t}\n}\n\nfunc (mcw *mockCloudWatch) PutMetricData(input *cloudwatch.PutMetricDataInput) (*cloudwatch.PutMetricDataOutput, error) {\n\tmcw.mtx.Lock()\n\tdefer mcw.mtx.Unlock()\n\tfor _, datum := range input.MetricData {\n\t\tif *datum.MetricName == metricNameToGenerateError {\n\t\t\treturn nil, errTest\n\t\t}\n\n\t\tif len(datum.Values) > 0 {\n\t\t\tfor _, v := range datum.Values {\n\t\t\t\tmcw.valuesReceived[*datum.MetricName] = append(mcw.valuesReceived[*datum.MetricName], *v)\n\t\t\t}\n\t\t} else {\n\t\t\tmcw.valuesReceived[*datum.MetricName] = append(mcw.valuesReceived[*datum.MetricName], *datum.Value)\n\t\t}\n\t\tmcw.dimensionsReceived[*datum.MetricName] = datum.Dimensions\n\t}\n\treturn nil, nil\n}\n\nfunc (mcw *mockCloudWatch) testDimensions(name string, labelValues ...string) error {\n\tmcw.mtx.RLock()\n\t_, hasValue := mcw.valuesReceived[name]\n\tif !hasValue {\n\t\treturn nil // nothing to check; 0 samples were received\n\t}\n\tdimensions, ok := mcw.dimensionsReceived[name]\n\tmcw.mtx.RUnlock()\n\n\tif !ok {\n\t\tif len(labelValues) > 0 {\n\t\t\treturn errors.New(\"Expected dimensions to be available, but none were\")\n\t\t}\n\t}\nLabelValues:\n\tfor i, j := 0, 0; i < len(labelValues); i, j = i+2, j+1 {\n\t\tname, value := labelValues[i], labelValues[i+1]\n\t\tfor _, dimension := range dimensions {\n\t\t\tif *dimension.Name == name {\n\t\t\t\tif *dimension.Value == value {\n\t\t\t\t\tbreak LabelValues\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn fmt.Errorf(\"could not find dimension with name %s and value %s\", name, value)\n\t}\n\n\treturn nil\n}\n\nfunc TestCounter(t *testing.T) {\n\tnamespace, name := \"abc\", \"def\"\n\tlabel, value := \"label\", \"value\"\n\tsvc := newMockCloudWatch()\n\tcw := New(namespace, svc, WithLogger(log.NewNopLogger()))\n\tcounter := cw.NewCounter(name).With(label, value)\n\tvaluef := func() float64 {\n\t\tif err := cw.Send(); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tsvc.mtx.RLock()\n\t\tdefer svc.mtx.RUnlock()\n\t\tvalue := svc.valuesReceived[name][len(svc.valuesReceived[name])-1]\n\t\tdelete(svc.valuesReceived, name)\n\n\t\treturn value\n\t}\n\tif err := teststat.TestCounter(counter, valuef); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif err := teststat.TestCounter(counter, valuef); err != nil {\n\t\tt.Fatal(\"Fill and flush counter 2nd time: \", err)\n\t}\n\tif err := svc.testDimensions(name, label, value); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestCounterLowSendConcurrency(t *testing.T) {\n\tnamespace := \"abc\"\n\tvar names, labels, values []string\n\tfor i := 1; i <= 45; i++ {\n\t\tnum := strconv.Itoa(i)\n\t\tnames = append(names, \"name\"+num)\n\t\tlabels = append(labels, \"label\"+num)\n\t\tvalues = append(values, \"value\"+num)\n\t}\n\tsvc := newMockCloudWatch()\n\tcw := New(namespace, svc,\n\t\tWithLogger(log.NewNopLogger()),\n\t\tWithConcurrentRequests(2),\n\t)\n\n\tcounters := make(map[string]metrics.Counter)\n\tvar wants []float64\n\tfor i, name := range names {\n\t\tcounters[name] = cw.NewCounter(name).With(labels[i], values[i])\n\t\twants = append(wants, teststat.FillCounter(counters[name]))\n\t}\n\n\tif err := cw.Send(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tfor i, name := range names {\n\t\tif l := len(svc.valuesReceived[name]); l == 0 && wants[i] == 0 {\n\t\t\tcontinue\n\t\t} else if l != 1 {\n\t\t\tt.Fatalf(\"one value expected, got %d\", l)\n\t\t}\n\n\t\tif svc.valuesReceived[name][0] != wants[i] {\n\t\t\tt.Fatalf(\"want %f, have %f\", wants[i], svc.valuesReceived[name])\n\t\t}\n\t\tif err := svc.testDimensions(name, labels[i], values[i]); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc TestGauge(t *testing.T) {\n\tnamespace, name := \"abc\", \"def\"\n\tlabel, value := \"label\", \"value\"\n\tsvc := newMockCloudWatch()\n\tcw := New(namespace, svc, WithLogger(log.NewNopLogger()))\n\tgauge := cw.NewGauge(name).With(label, value)\n\tvaluef := func() []float64 {\n\t\tif err := cw.Send(); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tsvc.mtx.RLock()\n\t\tdefer svc.mtx.RUnlock()\n\t\tres := svc.valuesReceived[name]\n\t\tdelete(svc.valuesReceived, name)\n\t\treturn res\n\t}\n\n\tif err := teststat.TestGauge(gauge, valuef); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif err := svc.testDimensions(name, label, value); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestHistogram(t *testing.T) {\n\tnamespace, name := \"abc\", \"def\"\n\tlabel, value := \"label\", \"value\"\n\tsvc := newMockCloudWatch()\n\tcw := New(namespace, svc, WithLogger(log.NewNopLogger()))\n\thistogram := cw.NewHistogram(name).With(label, value)\n\tn50 := fmt.Sprintf(\"%s_50\", name)\n\tn90 := fmt.Sprintf(\"%s_90\", name)\n\tn95 := fmt.Sprintf(\"%s_95\", name)\n\tn99 := fmt.Sprintf(\"%s_99\", name)\n\tquantiles := func() (p50, p90, p95, p99 float64) {\n\t\terr := cw.Send()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tsvc.mtx.RLock()\n\t\tdefer svc.mtx.RUnlock()\n\t\tif len(svc.valuesReceived[n50]) > 0 {\n\t\t\tp50 = svc.valuesReceived[n50][0]\n\t\t\tdelete(svc.valuesReceived, n50)\n\t\t}\n\n\t\tif len(svc.valuesReceived[n90]) > 0 {\n\t\t\tp90 = svc.valuesReceived[n90][0]\n\t\t\tdelete(svc.valuesReceived, n90)\n\t\t}\n\n\t\tif len(svc.valuesReceived[n95]) > 0 {\n\t\t\tp95 = svc.valuesReceived[n95][0]\n\t\t\tdelete(svc.valuesReceived, n95)\n\t\t}\n\n\t\tif len(svc.valuesReceived[n99]) > 0 {\n\t\t\tp99 = svc.valuesReceived[n99][0]\n\t\t\tdelete(svc.valuesReceived, n99)\n\t\t}\n\t\treturn\n\t}\n\tif err := teststat.TestHistogram(histogram, quantiles, 0.01); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif err := svc.testDimensions(n50, label, value); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif err := svc.testDimensions(n90, label, value); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif err := svc.testDimensions(n95, label, value); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif err := svc.testDimensions(n99, label, value); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// now test with only 2 custom percentiles\n\t//\n\tsvc = newMockCloudWatch()\n\tcw = New(namespace, svc, WithLogger(log.NewNopLogger()), WithPercentiles(0.50, 0.90))\n\thistogram = cw.NewHistogram(name).With(label, value)\n\n\tcustomQuantiles := func() (p50, p90, p95, p99 float64) {\n\t\terr := cw.Send()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tsvc.mtx.RLock()\n\t\tdefer svc.mtx.RUnlock()\n\t\tif len(svc.valuesReceived[n50]) > 0 {\n\t\t\tp50 = svc.valuesReceived[n50][0]\n\t\t\tdelete(svc.valuesReceived, n50)\n\t\t}\n\t\tif len(svc.valuesReceived[n90]) > 0 {\n\t\t\tp90 = svc.valuesReceived[n90][0]\n\t\t\tdelete(svc.valuesReceived, n90)\n\t\t}\n\n\t\t// our teststat.TestHistogram wants us to give p95 and p99,\n\t\t// but with custom percentiles we don't have those.\n\t\t// So fake them. Maybe we should make teststat.nvq() public and use that?\n\t\tp95 = 541.121341\n\t\tp99 = 558.158697\n\n\t\t// but fail if they are actually set (because that would mean the\n\t\t// WithPercentiles() is not respected)\n\t\tif _, isSet := svc.valuesReceived[n95]; isSet {\n\t\t\tt.Fatal(\"p95 should not be set\")\n\t\t}\n\t\tif _, isSet := svc.valuesReceived[n99]; isSet {\n\t\t\tt.Fatal(\"p99 should not be set\")\n\t\t}\n\t\treturn\n\t}\n\tif err := teststat.TestHistogram(histogram, customQuantiles, 0.01); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif err := svc.testDimensions(n50, label, value); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif err := svc.testDimensions(n90, label, value); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif err := svc.testDimensions(n95, label, value); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif err := svc.testDimensions(n99, label, value); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestErrorLog(t *testing.T) {\n\tnamespace := \"abc\"\n\tsvc := newMockCloudWatch()\n\tcw := New(namespace, svc, WithLogger(log.NewNopLogger()))\n\tcw.NewGauge(metricNameToGenerateError).Set(123)\n\tif err := cw.Send(); err != errTest {\n\t\tt.Fatal(\"Expected error, but didn't get one\")\n\t}\n}\n"
  },
  {
    "path": "metrics/cloudwatch2/cloudwatch2.go",
    "content": "// Package cloudwatch2 emits all data as a StatisticsSet (rather than\n// a singular Value) to CloudWatch via the aws-sdk-go-v2 SDK.\npackage cloudwatch2\n\nimport (\n\t\"context\"\n\t\"math\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/aws/aws-sdk-go-v2/aws\"\n\t\"github.com/aws/aws-sdk-go-v2/service/cloudwatch\"\n\t\"github.com/aws/aws-sdk-go-v2/service/cloudwatch/types\"\n\t\"golang.org/x/sync/errgroup\"\n\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/internal/convert\"\n\t\"github.com/go-kit/kit/metrics/internal/lv\"\n\t\"github.com/go-kit/log\"\n)\n\nconst (\n\tmaxConcurrentRequests = 20\n)\n\n// CloudWatchAPI is an interface that defines the set of Amazon CloudWatch API operations required by CloudWatch.\ntype CloudWatchAPI interface {\n\tPutMetricData(ctx context.Context, params *cloudwatch.PutMetricDataInput, optFns ...func(*cloudwatch.Options)) (*cloudwatch.PutMetricDataOutput, error)\n}\n\n// CloudWatch receives metrics observations and forwards them to CloudWatch.\n// Create a CloudWatch object, use it to create metrics, and pass those metrics as\n// dependencies to the components that will use them.\n//\n// To regularly report metrics to CloudWatch, use the WriteLoop helper method.\ntype CloudWatch struct {\n\tmtx                   sync.RWMutex\n\tsem                   chan struct{}\n\tnamespace             string\n\tsvc                   CloudWatchAPI\n\tcounters              *lv.Space\n\tlogger                log.Logger\n\tnumConcurrentRequests int\n}\n\n// Option is a function adapter to change config of the CloudWatch struct\ntype Option func(*CloudWatch)\n\n// WithLogger sets the Logger that will receive error messages generated\n// during the WriteLoop. By default, no logger is used.\nfunc WithLogger(logger log.Logger) Option {\n\treturn func(cw *CloudWatch) {\n\t\tcw.logger = logger\n\t}\n}\n\n// WithConcurrentRequests sets the upper limit on how many\n// cloudwatch.PutMetricDataRequest may be under way at any\n// given time. If n is greater than 20, 20 is used. By default,\n// the max is set at 10 concurrent requests.\nfunc WithConcurrentRequests(n int) Option {\n\treturn func(cw *CloudWatch) {\n\t\tif n > maxConcurrentRequests {\n\t\t\tn = maxConcurrentRequests\n\t\t}\n\t\tcw.numConcurrentRequests = n\n\t}\n}\n\n// New returns a CloudWatch object that may be used to create metrics.\n// Namespace is applied to all created metrics and maps to the CloudWatch namespace.\n// Callers must ensure that regular calls to Send are performed, either\n// manually or with one of the helper methods.\nfunc New(namespace string, svc CloudWatchAPI, options ...Option) *CloudWatch {\n\tcw := &CloudWatch{\n\t\tnamespace:             namespace,\n\t\tsvc:                   svc,\n\t\tcounters:              lv.NewSpace(),\n\t\tnumConcurrentRequests: 10,\n\t\tlogger:                log.NewNopLogger(),\n\t}\n\n\tfor _, optFunc := range options {\n\t\toptFunc(cw)\n\t}\n\n\tcw.sem = make(chan struct{}, cw.numConcurrentRequests)\n\n\treturn cw\n}\n\n// NewCounter returns a counter. Observations are aggregated and emitted once\n// per write invocation.\nfunc (cw *CloudWatch) NewCounter(name string) metrics.Counter {\n\treturn &Counter{\n\t\tname: name,\n\t\tobs:  cw.counters.Observe,\n\t}\n}\n\n// NewGauge returns an gauge. Under the covers, there is no distinctions\n// in CloudWatch for how Counters/Histograms/Gauges are reported, so this\n// just wraps a cloudwatch2.Counter.\nfunc (cw *CloudWatch) NewGauge(name string) metrics.Gauge {\n\treturn convert.NewCounterAsGauge(cw.NewCounter(name))\n}\n\n// NewHistogram returns a histogram. Under the covers, there is no distinctions\n// in CloudWatch for how Counters/Histograms/Gauges are reported, so this\n// just wraps a cloudwatch2.Counter.\nfunc (cw *CloudWatch) NewHistogram(name string) metrics.Histogram {\n\treturn convert.NewCounterAsHistogram(cw.NewCounter(name))\n}\n\n// WriteLoop is a helper method that invokes Send every time the passed\n// channel fires. This method blocks until ctx is canceled, so clients\n// probably want to run it in its own goroutine. For typical usage, create a\n// time.Ticker and pass its C channel to this method.\nfunc (cw *CloudWatch) WriteLoop(ctx context.Context, c <-chan time.Time) {\n\tfor {\n\t\tselect {\n\t\tcase <-c:\n\t\t\tif err := cw.Send(); err != nil {\n\t\t\t\tcw.logger.Log(\"during\", \"Send\", \"err\", err)\n\t\t\t}\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// Send will fire an API request to CloudWatch with the latest stats for\n// all metrics. It is preferred that the WriteLoop method is used.\nfunc (cw *CloudWatch) Send() error {\n\tcw.mtx.RLock()\n\tdefer cw.mtx.RUnlock()\n\tnow := time.Now()\n\n\tvar datums []types.MetricDatum\n\n\tcw.counters.Reset().Walk(func(name string, lvs lv.LabelValues, values []float64) bool {\n\t\tdatums = append(datums, types.MetricDatum{\n\t\t\tMetricName:      aws.String(name),\n\t\t\tDimensions:      makeDimensions(lvs...),\n\t\t\tStatisticValues: stats(values),\n\t\t\tTimestamp:       aws.Time(now),\n\t\t})\n\t\treturn true\n\t})\n\n\tvar batches [][]types.MetricDatum\n\tfor len(datums) > 0 {\n\t\tvar batch []types.MetricDatum\n\t\tlim := len(datums)\n\t\tif lim > maxConcurrentRequests {\n\t\t\tlim = maxConcurrentRequests\n\t\t}\n\t\tbatch, datums = datums[:lim], datums[lim:]\n\t\tbatches = append(batches, batch)\n\t}\n\n\tvar g errgroup.Group\n\tfor _, batch := range batches {\n\t\tbatch := batch\n\t\tg.Go(func() error {\n\t\t\tcw.sem <- struct{}{}\n\t\t\tdefer func() {\n\t\t\t\t<-cw.sem\n\t\t\t}()\n\t\t\t_, err := cw.svc.PutMetricData(context.TODO(), &cloudwatch.PutMetricDataInput{\n\t\t\t\tNamespace:  aws.String(cw.namespace),\n\t\t\t\tMetricData: batch,\n\t\t\t})\n\t\t\treturn err\n\t\t})\n\t}\n\treturn g.Wait()\n}\n\nvar zero = float64(0.0)\n\n// Just build this once to reduce construction costs whenever\n// someone does a Send with no aggregated values.\nvar zeros = types.StatisticSet{\n\tMaximum:     &zero,\n\tMinimum:     &zero,\n\tSum:         &zero,\n\tSampleCount: &zero,\n}\n\nfunc stats(a []float64) *types.StatisticSet {\n\tcount := float64(len(a))\n\tif count == 0 {\n\t\treturn &zeros\n\t}\n\n\tvar sum float64\n\tvar min = math.MaxFloat64\n\tvar max = math.MaxFloat64 * -1\n\tfor _, f := range a {\n\t\tsum += f\n\t\tif f < min {\n\t\t\tmin = f\n\t\t}\n\t\tif f > max {\n\t\t\tmax = f\n\t\t}\n\t}\n\n\treturn &types.StatisticSet{\n\t\tMaximum:     &max,\n\t\tMinimum:     &min,\n\t\tSum:         &sum,\n\t\tSampleCount: &count,\n\t}\n}\n\nfunc makeDimensions(labelValues ...string) []types.Dimension {\n\tdimensions := make([]types.Dimension, len(labelValues)/2)\n\tfor i, j := 0, 0; i < len(labelValues); i, j = i+2, j+1 {\n\t\tdimensions[j] = types.Dimension{\n\t\t\tName:  aws.String(labelValues[i]),\n\t\t\tValue: aws.String(labelValues[i+1]),\n\t\t}\n\t}\n\treturn dimensions\n}\n\ntype observeFunc func(name string, lvs lv.LabelValues, value float64)\n\n// Counter is a counter. Observations are forwarded to a node\n// object, and aggregated per timeseries.\ntype Counter struct {\n\tname string\n\tlvs  lv.LabelValues\n\tobs  observeFunc\n}\n\n// With implements metrics.Counter.\nfunc (c *Counter) With(labelValues ...string) metrics.Counter {\n\treturn &Counter{\n\t\tname: c.name,\n\t\tlvs:  c.lvs.With(labelValues...),\n\t\tobs:  c.obs,\n\t}\n}\n\n// Add implements metrics.Counter.\nfunc (c *Counter) Add(delta float64) {\n\tc.obs(c.name, c.lvs, delta)\n}\n"
  },
  {
    "path": "metrics/cloudwatch2/cloudwatch2_test.go",
    "content": "package cloudwatch2\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/aws/aws-sdk-go-v2/service/cloudwatch\"\n\t\"github.com/aws/aws-sdk-go-v2/service/cloudwatch/types\"\n)\n\nfunc TestStats(t *testing.T) {\n\ttestCases := []struct {\n\t\tname string\n\t\tvals []float64\n\t\txMin float64\n\t\txMax float64\n\t\txSum float64\n\t\txCt  float64\n\t}{\n\t\t{\n\t\t\t\"empty\",\n\t\t\t[]float64{},\n\t\t\t0.0,\n\t\t\t0.0,\n\t\t\t0.0,\n\t\t\t0.0,\n\t\t},\n\t\t{\n\t\t\t\"single\",\n\t\t\t[]float64{3.1416},\n\t\t\t3.1416,\n\t\t\t3.1416,\n\t\t\t3.1416,\n\t\t\t1.0,\n\t\t},\n\t\t{\n\t\t\t\"double\",\n\t\t\t[]float64{1.0, 9.0},\n\t\t\t1.0,\n\t\t\t9.0,\n\t\t\t10.0,\n\t\t\t2.0,\n\t\t},\n\t\t{\n\t\t\t\"multiple\",\n\t\t\t[]float64{5.0, 1.0, 9.0, 5.0},\n\t\t\t1.0,\n\t\t\t9.0,\n\t\t\t20.0,\n\t\t\t4.0,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\ts := stats(tc.vals)\n\t\t\tif tc.xMin != *s.Minimum {\n\t\t\t\tt.Errorf(\"expected [%f]: %f\\n\", tc.xMin, *s.Minimum)\n\t\t\t}\n\t\t\tif tc.xMax != *s.Maximum {\n\t\t\t\tt.Errorf(\"expected [%f]: %f\\n\", tc.xMax, *s.Maximum)\n\t\t\t}\n\t\t\tif tc.xSum != *s.Sum {\n\t\t\t\tt.Errorf(\"expected [%f]: %f\\n\", tc.xSum, *s.Sum)\n\t\t\t}\n\t\t\tif tc.xCt != *s.SampleCount {\n\t\t\t\tt.Errorf(\"expected [%f]: %f\\n\", tc.xCt, *s.SampleCount)\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype mockCloudWatch struct {\n\tCloudWatchAPI\n\tlatestName string\n\tlatestData []types.MetricDatum\n}\n\nfunc (mcw *mockCloudWatch) PutMetricData(ctx context.Context, params *cloudwatch.PutMetricDataInput, optFns ...func(*cloudwatch.Options)) (*cloudwatch.PutMetricDataOutput, error) {\n\tmcw.latestName = *params.Namespace\n\tmcw.latestData = params.MetricData\n\n\treturn nil, nil\n}\n\nfunc TestSend(t *testing.T) {\n\tns := \"example-namespace\"\n\tsvc := &mockCloudWatch{}\n\tcw := New(ns, svc)\n\n\tc := cw.NewCounter(\"c\").With(\"charlie\", \"cat\")\n\th := cw.NewHistogram(\"h\").With(\"hotel\", \"horse\")\n\tg := cw.NewGauge(\"g\").With(\"golf\", \"giraffe\")\n\n\tc.Add(4.0)\n\tc.Add(5.0)\n\tc.Add(6.0)\n\th.Observe(3.0)\n\th.Observe(5.0)\n\th.Observe(7.0)\n\tg.Set(2.0)\n\tg.Set(5.0)\n\tg.Set(8.0)\n\n\terr := cw.Send()\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected: %v\\n\", err)\n\t}\n\n\tif ns != svc.latestName {\n\t\tt.Errorf(\"expected namespace %q; not %q\\n\", ns, svc.latestName)\n\t}\n\n\tif len(svc.latestData) != 3 {\n\t\tt.Errorf(\"expected 3 datums: %v\\n\", svc.latestData)\n\t}\n\tfor _, datum := range svc.latestData {\n\t\tinitial := *datum.MetricName\n\t\tif len(datum.Dimensions) != 1 {\n\t\t\tt.Errorf(\"expected 1 dimension: %v\\n\", datum)\n\t\t}\n\t\tif !strings.HasPrefix(*datum.Dimensions[0].Name, initial) {\n\t\t\tt.Errorf(\"expected %q in Name of %v\\n\", initial, datum.Dimensions)\n\t\t}\n\t\tif !strings.HasPrefix(*datum.Dimensions[0].Value, initial) {\n\t\t\tt.Errorf(\"expected %q in Value of %v\\n\", initial, datum.Dimensions)\n\t\t}\n\t\tif datum.StatisticValues == nil {\n\t\t\tt.Errorf(\"expected StatisticValues in %v\\n\", datum)\n\t\t}\n\t\tif *datum.StatisticValues.Sum != 15.0 {\n\t\t\tt.Errorf(\"expected 15.0 for Sum in %v\\n\", datum)\n\t\t}\n\t\tif *datum.StatisticValues.SampleCount != 3.0 {\n\t\t\tt.Errorf(\"expected 3.0 for SampleCount in %v\\n\", datum)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "metrics/discard/discard.go",
    "content": "// Package discard provides a no-op metrics backend.\npackage discard\n\nimport \"github.com/go-kit/kit/metrics\"\n\ntype counter struct{}\n\n// NewCounter returns a new no-op counter.\nfunc NewCounter() metrics.Counter { return counter{} }\n\n// With implements Counter.\nfunc (c counter) With(labelValues ...string) metrics.Counter { return c }\n\n// Add implements Counter.\nfunc (c counter) Add(delta float64) {}\n\ntype gauge struct{}\n\n// NewGauge returns a new no-op gauge.\nfunc NewGauge() metrics.Gauge { return gauge{} }\n\n// With implements Gauge.\nfunc (g gauge) With(labelValues ...string) metrics.Gauge { return g }\n\n// Set implements Gauge.\nfunc (g gauge) Set(value float64) {}\n\n// Add implements metrics.Gauge.\nfunc (g gauge) Add(delta float64) {}\n\ntype histogram struct{}\n\n// NewHistogram returns a new no-op histogram.\nfunc NewHistogram() metrics.Histogram { return histogram{} }\n\n// With implements Histogram.\nfunc (h histogram) With(labelValues ...string) metrics.Histogram { return h }\n\n// Observe implements histogram.\nfunc (h histogram) Observe(value float64) {}\n"
  },
  {
    "path": "metrics/doc.go",
    "content": "// Package metrics provides a framework for application instrumentation. It's\n// primarily designed to help you get started with good and robust\n// instrumentation, and to help you migrate from a less-capable system like\n// Graphite to a more-capable system like Prometheus. If your organization has\n// already standardized on an instrumentation system like Prometheus, and has no\n// plans to change, it may make sense to use that system's instrumentation\n// library directly.\n//\n// This package provides three core metric abstractions (Counter, Gauge, and\n// Histogram) and implementations for almost all common instrumentation\n// backends. Each metric has an observation method (Add, Set, or Observe,\n// respectively) used to record values, and a With method to \"scope\" the\n// observation by various parameters. For example, you might have a Histogram to\n// record request durations, parameterized by the method that's being called.\n//\n//    var requestDuration metrics.Histogram\n//    // ...\n//    requestDuration.With(\"method\", \"MyMethod\").Observe(time.Since(begin))\n//\n// This allows a single high-level metrics object (requestDuration) to work with\n// many code paths somewhat dynamically. The concept of With is fully supported\n// in some backends like Prometheus, and not supported in other backends like\n// Graphite. So, With may be a no-op, depending on the concrete implementation\n// you choose. Please check the implementation to know for sure. For\n// implementations that don't provide With, it's necessary to fully parameterize\n// each metric in the metric name, e.g.\n//\n//    // Statsd\n//    c := statsd.NewCounter(\"request_duration_MyMethod_200\")\n//    c.Add(1)\n//\n//    // Prometheus\n//    c := prometheus.NewCounter(stdprometheus.CounterOpts{\n//        Name: \"request_duration\",\n//        ...\n//    }, []string{\"method\", \"status_code\"})\n//    c.With(\"method\", \"MyMethod\", \"status_code\", strconv.Itoa(code)).Add(1)\n//\n// Usage\n//\n// Metrics are dependencies, and should be passed to the components that need\n// them in the same way you'd construct and pass a database handle, or reference\n// to another component. Metrics should *not* be created in the global scope.\n// Instead, instantiate metrics in your func main, using whichever concrete\n// implementation is appropriate for your organization.\n//\n//    latency := prometheus.NewSummaryFrom(stdprometheus.SummaryOpts{\n//        Namespace: \"myteam\",\n//        Subsystem: \"foosvc\",\n//        Name:      \"request_latency_seconds\",\n//        Help:      \"Incoming request latency in seconds.\",\n//    }, []string{\"method\", \"status_code\"})\n//\n// Write your components to take the metrics they will use as parameters to\n// their constructors. Use the interface types, not the concrete types. That is,\n//\n//    // NewAPI takes metrics.Histogram, not *prometheus.Summary\n//    func NewAPI(s Store, logger log.Logger, latency metrics.Histogram) *API {\n//        // ...\n//    }\n//\n//    func (a *API) ServeFoo(w http.ResponseWriter, r *http.Request) {\n//        begin := time.Now()\n//        // ...\n//        a.latency.Observe(time.Since(begin).Seconds())\n//    }\n//\n// Finally, pass the metrics as dependencies when building your object graph.\n// This should happen in func main, not in the global scope.\n//\n//    api := NewAPI(store, logger, latency)\n//    http.ListenAndServe(\"/\", api)\n//\n// Note that metrics are \"write-only\" interfaces.\n//\n// Implementation details\n//\n// All metrics are safe for concurrent use. Considerable design influence has\n// been taken from https://github.com/codahale/metrics and\n// https://prometheus.io.\n//\n// Each telemetry system has different semantics for label values, push vs.\n// pull, support for histograms, etc. These properties influence the design of\n// their respective packages. This table attempts to summarize the key points of\n// distinction.\n//\n//    SYSTEM      DIM  COUNTERS               GAUGES                 HISTOGRAMS\n//    dogstatsd   n    batch, push-aggregate  batch, push-aggregate  native, batch, push-each\n//    statsd      1    batch, push-aggregate  batch, push-aggregate  native, batch, push-each\n//    graphite    1    batch, push-aggregate  batch, push-aggregate  synthetic, batch, push-aggregate\n//    expvar      1    atomic                 atomic                 synthetic, batch, in-place expose\n//    influx      n    custom                 custom                 custom\n//    prometheus  n    native                 native                 native\n//    pcp         1    native                 native                 native\n//    cloudwatch  n    batch push-aggregate   batch push-aggregate   synthetic, batch, push-aggregate\n//\npackage metrics\n"
  },
  {
    "path": "metrics/dogstatsd/dogstatsd.go",
    "content": "// Package dogstatsd provides a DogStatsD backend for package metrics. It's very\n// similar to StatsD, but supports arbitrary tags per-metric, which map to Go\n// kit's label values. So, while label values are no-ops in StatsD, they are\n// supported here. For more details, see the documentation at\n// http://docs.datadoghq.com/guides/dogstatsd/.\n//\n// This package batches observations and emits them on some schedule to the\n// remote server. This is useful even if you connect to your DogStatsD server\n// over UDP. Emitting one network packet per observation can quickly overwhelm\n// even the fastest internal network.\npackage dogstatsd\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/rand\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/generic\"\n\t\"github.com/go-kit/kit/metrics/internal/lv\"\n\t\"github.com/go-kit/kit/metrics/internal/ratemap\"\n\t\"github.com/go-kit/kit/util/conn\"\n\t\"github.com/go-kit/log\"\n)\n\n// Dogstatsd receives metrics observations and forwards them to a DogStatsD\n// server. Create a Dogstatsd object, use it to create metrics, and pass those\n// metrics as dependencies to the components that will use them.\n//\n// All metrics are buffered until WriteTo is called. Counters and gauges are\n// aggregated into a single observation per timeseries per write. Timings and\n// histograms are buffered but not aggregated.\n//\n// To regularly report metrics to an io.Writer, use the WriteLoop helper method.\n// To send to a DogStatsD server, use the SendLoop helper method.\ntype Dogstatsd struct {\n\tmtx        sync.RWMutex\n\tprefix     string\n\trates      *ratemap.RateMap\n\tcounters   *lv.Space\n\tgauges     map[string]*gaugeNode\n\ttimings    *lv.Space\n\thistograms *lv.Space\n\tlogger     log.Logger\n\tlvs        lv.LabelValues\n}\n\n// New returns a Dogstatsd object that may be used to create metrics. Prefix is\n// applied to all created metrics. Callers must ensure that regular calls to\n// WriteTo are performed, either manually or with one of the helper methods.\nfunc New(prefix string, logger log.Logger, lvs ...string) *Dogstatsd {\n\tif len(lvs)%2 != 0 {\n\t\tpanic(\"odd number of LabelValues; programmer error!\")\n\t}\n\treturn &Dogstatsd{\n\t\tprefix:     prefix,\n\t\trates:      ratemap.New(),\n\t\tcounters:   lv.NewSpace(),\n\t\tgauges:     map[string]*gaugeNode{},\n\t\ttimings:    lv.NewSpace(),\n\t\thistograms: lv.NewSpace(),\n\t\tlogger:     logger,\n\t\tlvs:        lvs,\n\t}\n}\n\n// NewCounter returns a counter, sending observations to this Dogstatsd object.\nfunc (d *Dogstatsd) NewCounter(name string, sampleRate float64) *Counter {\n\td.rates.Set(name, sampleRate)\n\treturn &Counter{\n\t\tname: name,\n\t\tobs:  sampleObservations(d.counters.Observe, sampleRate),\n\t}\n}\n\n// NewGauge returns a gauge, sending observations to this Dogstatsd object.\nfunc (d *Dogstatsd) NewGauge(name string) *Gauge {\n\td.mtx.Lock()\n\tn, ok := d.gauges[name]\n\tif !ok {\n\t\tn = &gaugeNode{gauge: &Gauge{g: generic.NewGauge(name), ddog: d}}\n\t\td.gauges[name] = n\n\t}\n\td.mtx.Unlock()\n\treturn n.gauge\n}\n\n// NewTiming returns a histogram whose observations are interpreted as\n// millisecond durations, and are forwarded to this Dogstatsd object.\nfunc (d *Dogstatsd) NewTiming(name string, sampleRate float64) *Timing {\n\td.rates.Set(name, sampleRate)\n\treturn &Timing{\n\t\tname: name,\n\t\tobs:  sampleObservations(d.timings.Observe, sampleRate),\n\t}\n}\n\n// NewHistogram returns a histogram whose observations are of an unspecified\n// unit, and are forwarded to this Dogstatsd object.\nfunc (d *Dogstatsd) NewHistogram(name string, sampleRate float64) *Histogram {\n\td.rates.Set(name, sampleRate)\n\treturn &Histogram{\n\t\tname: name,\n\t\tobs:  sampleObservations(d.histograms.Observe, sampleRate),\n\t}\n}\n\n// WriteLoop is a helper method that invokes WriteTo to the passed writer every\n// time the passed channel fires. This method blocks until ctx is canceled,\n// so clients probably want to run it in its own goroutine. For typical\n// usage, create a time.Ticker and pass its C channel to this method.\nfunc (d *Dogstatsd) WriteLoop(ctx context.Context, c <-chan time.Time, w io.Writer) {\n\tfor {\n\t\tselect {\n\t\tcase <-c:\n\t\t\tif _, err := d.WriteTo(w); err != nil {\n\t\t\t\td.logger.Log(\"during\", \"WriteTo\", \"err\", err)\n\t\t\t}\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// SendLoop is a helper method that wraps WriteLoop, passing a managed\n// connection to the network and address. Like WriteLoop, this method blocks\n// until ctx is canceled, so clients probably want to start it in its own\n// goroutine. For typical usage, create a time.Ticker and pass its C channel to\n// this method.\nfunc (d *Dogstatsd) SendLoop(ctx context.Context, c <-chan time.Time, network, address string) {\n\td.WriteLoop(ctx, c, conn.NewDefaultManager(network, address, d.logger))\n}\n\n// WriteTo flushes the buffered content of the metrics to the writer, in\n// DogStatsD format. WriteTo abides best-effort semantics, so observations are\n// lost if there is a problem with the write. Clients should be sure to call\n// WriteTo regularly, ideally through the WriteLoop or SendLoop helper methods.\nfunc (d *Dogstatsd) WriteTo(w io.Writer) (count int64, err error) {\n\tvar n int\n\n\td.counters.Reset().Walk(func(name string, lvs lv.LabelValues, values []float64) bool {\n\t\tn, err = fmt.Fprintf(w, \"%s%s:%f|c%s%s\\n\", d.prefix, name, sum(values), sampling(d.rates.Get(name)), d.tagValues(lvs))\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tcount += int64(n)\n\t\treturn true\n\t})\n\tif err != nil {\n\t\treturn count, err\n\t}\n\n\td.mtx.RLock()\n\tfor _, root := range d.gauges {\n\t\troot.walk(func(name string, lvs lv.LabelValues, value float64) bool {\n\t\t\tn, err = fmt.Fprintf(w, \"%s%s:%f|g%s\\n\", d.prefix, name, value, d.tagValues(lvs))\n\t\t\tif err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tcount += int64(n)\n\t\t\treturn true\n\t\t})\n\t}\n\td.mtx.RUnlock()\n\n\td.timings.Reset().Walk(func(name string, lvs lv.LabelValues, values []float64) bool {\n\t\tsampleRate := d.rates.Get(name)\n\t\tfor _, value := range values {\n\t\t\tn, err = fmt.Fprintf(w, \"%s%s:%f|ms%s%s\\n\", d.prefix, name, value, sampling(sampleRate), d.tagValues(lvs))\n\t\t\tif err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tcount += int64(n)\n\t\t}\n\t\treturn true\n\t})\n\tif err != nil {\n\t\treturn count, err\n\t}\n\n\td.histograms.Reset().Walk(func(name string, lvs lv.LabelValues, values []float64) bool {\n\t\tsampleRate := d.rates.Get(name)\n\t\tfor _, value := range values {\n\t\t\tn, err = fmt.Fprintf(w, \"%s%s:%f|h%s%s\\n\", d.prefix, name, value, sampling(sampleRate), d.tagValues(lvs))\n\t\t\tif err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tcount += int64(n)\n\t\t}\n\t\treturn true\n\t})\n\tif err != nil {\n\t\treturn count, err\n\t}\n\n\treturn count, err\n}\n\nfunc sum(a []float64) float64 {\n\tvar v float64\n\tfor _, f := range a {\n\t\tv += f\n\t}\n\treturn v\n}\n\nfunc sampling(r float64) string {\n\tvar sv string\n\tif r < 1.0 {\n\t\tsv = fmt.Sprintf(\"|@%f\", r)\n\t}\n\treturn sv\n}\n\nfunc (d *Dogstatsd) tagValues(labelValues []string) string {\n\tif len(labelValues) == 0 && len(d.lvs) == 0 {\n\t\treturn \"\"\n\t}\n\tif len(labelValues)%2 != 0 {\n\t\tpanic(\"tagValues received a labelValues with an odd number of strings\")\n\t}\n\tpairs := make([]string, 0, (len(d.lvs)+len(labelValues))/2)\n\tfor i := 0; i < len(d.lvs); i += 2 {\n\t\tpairs = append(pairs, d.lvs[i]+\":\"+d.lvs[i+1])\n\t}\n\tfor i := 0; i < len(labelValues); i += 2 {\n\t\tpairs = append(pairs, labelValues[i]+\":\"+labelValues[i+1])\n\t}\n\treturn \"|#\" + strings.Join(pairs, \",\")\n}\n\ntype observeFunc func(name string, lvs lv.LabelValues, value float64)\n\n// sampleObservations returns a modified observeFunc that samples observations.\nfunc sampleObservations(obs observeFunc, sampleRate float64) observeFunc {\n\tif sampleRate >= 1 {\n\t\treturn obs\n\t}\n\treturn func(name string, lvs lv.LabelValues, value float64) {\n\t\tif rand.Float64() > sampleRate {\n\t\t\treturn\n\t\t}\n\t\tobs(name, lvs, value)\n\t}\n}\n\n// Counter is a DogStatsD counter. Observations are forwarded to a Dogstatsd\n// object, and aggregated (summed) per timeseries.\ntype Counter struct {\n\tname string\n\tlvs  lv.LabelValues\n\tobs  observeFunc\n}\n\n// With implements metrics.Counter.\nfunc (c *Counter) With(labelValues ...string) metrics.Counter {\n\treturn &Counter{\n\t\tname: c.name,\n\t\tlvs:  c.lvs.With(labelValues...),\n\t\tobs:  c.obs,\n\t}\n}\n\n// Add implements metrics.Counter.\nfunc (c *Counter) Add(delta float64) {\n\tc.obs(c.name, c.lvs, delta)\n}\n\n// Gauge is a DogStatsD gauge. Observations are forwarded to a Dogstatsd\n// object, and aggregated (the last observation selected) per timeseries.\ntype Gauge struct {\n\tg    *generic.Gauge\n\tddog *Dogstatsd\n\tset  int32\n}\n\n// With implements metrics.Gauge.\nfunc (g *Gauge) With(labelValues ...string) metrics.Gauge {\n\tg.ddog.mtx.RLock()\n\tnode := g.ddog.gauges[g.g.Name]\n\tg.ddog.mtx.RUnlock()\n\n\tga := &Gauge{g: g.g.With(labelValues...).(*generic.Gauge), ddog: g.ddog}\n\treturn node.addGauge(ga, ga.g.LabelValues())\n}\n\n// Set implements metrics.Gauge.\nfunc (g *Gauge) Set(value float64) {\n\tg.g.Set(value)\n\tg.touch()\n}\n\n// Add implements metrics.Gauge.\nfunc (g *Gauge) Add(delta float64) {\n\tg.g.Add(delta)\n\tg.touch()\n}\n\n// Timing is a DogStatsD timing, or metrics.Histogram. Observations are\n// forwarded to a Dogstatsd object, and collected (but not aggregated) per\n// timeseries.\ntype Timing struct {\n\tname string\n\tlvs  lv.LabelValues\n\tobs  observeFunc\n}\n\n// With implements metrics.Timing.\nfunc (t *Timing) With(labelValues ...string) metrics.Histogram {\n\treturn &Timing{\n\t\tname: t.name,\n\t\tlvs:  t.lvs.With(labelValues...),\n\t\tobs:  t.obs,\n\t}\n}\n\n// Observe implements metrics.Histogram. Value is interpreted as milliseconds.\nfunc (t *Timing) Observe(value float64) {\n\tt.obs(t.name, t.lvs, value)\n}\n\n// Histogram is a DogStatsD histrogram. Observations are forwarded to a\n// Dogstatsd object, and collected (but not aggregated) per timeseries.\ntype Histogram struct {\n\tname string\n\tlvs  lv.LabelValues\n\tobs  observeFunc\n}\n\n// With implements metrics.Histogram.\nfunc (h *Histogram) With(labelValues ...string) metrics.Histogram {\n\treturn &Histogram{\n\t\tname: h.name,\n\t\tlvs:  h.lvs.With(labelValues...),\n\t\tobs:  h.obs,\n\t}\n}\n\n// Observe implements metrics.Histogram.\nfunc (h *Histogram) Observe(value float64) {\n\th.obs(h.name, h.lvs, value)\n}\n\ntype pair struct{ label, value string }\n\ntype gaugeNode struct {\n\tmtx      sync.RWMutex\n\tgauge    *Gauge\n\tchildren map[pair]*gaugeNode\n}\n\nfunc (n *gaugeNode) addGauge(g *Gauge, lvs lv.LabelValues) *Gauge {\n\tn.mtx.Lock()\n\tdefer n.mtx.Unlock()\n\tif len(lvs) == 0 {\n\t\tif n.gauge == nil {\n\t\t\tn.gauge = g\n\t\t}\n\t\treturn n.gauge\n\t}\n\tif len(lvs) < 2 {\n\t\tpanic(\"too few LabelValues; programmer error!\")\n\t}\n\thead, tail := pair{lvs[0], lvs[1]}, lvs[2:]\n\tif n.children == nil {\n\t\tn.children = map[pair]*gaugeNode{}\n\t}\n\tchild, ok := n.children[head]\n\tif !ok {\n\t\tchild = &gaugeNode{}\n\t\tn.children[head] = child\n\t}\n\treturn child.addGauge(g, tail)\n}\n\nfunc (n *gaugeNode) walk(fn func(string, lv.LabelValues, float64) bool) bool {\n\tn.mtx.RLock()\n\tdefer n.mtx.RUnlock()\n\tif n.gauge != nil {\n\t\tvalue, ok := n.gauge.read()\n\t\tif ok && !fn(n.gauge.g.Name, n.gauge.g.LabelValues(), value) {\n\t\t\treturn false\n\t\t}\n\t}\n\tfor _, child := range n.children {\n\t\tif !child.walk(fn) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (g *Gauge) touch() {\n\tatomic.StoreInt32(&(g.set), 1)\n}\n\nfunc (g *Gauge) read() (float64, bool) {\n\tset := atomic.SwapInt32(&(g.set), 0)\n\treturn g.g.Value(), set != 0\n}\n"
  },
  {
    "path": "metrics/dogstatsd/dogstatsd_test.go",
    "content": "package dogstatsd\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-kit/kit/metrics/teststat\"\n\t\"github.com/go-kit/log\"\n)\n\nfunc TestCounter(t *testing.T) {\n\tprefix, name := \"abc.\", \"def\"\n\tlabel, value := \"label\", \"value\"\n\tregex := `^` + prefix + name + `:([0-9\\.]+)\\|c\\|#` + label + `:` + value + `$`\n\td := New(prefix, log.NewNopLogger())\n\tcounter := d.NewCounter(name, 1.0).With(label, value)\n\tvaluef := teststat.SumLines(d, regex)\n\tif err := teststat.TestCounter(counter, valuef); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestCounterSampled(t *testing.T) {\n\t// This will involve multiplying the observed sum by the inverse of the\n\t// sample rate and checking against the expected value within some\n\t// tolerance.\n\tt.Skip(\"TODO\")\n}\n\nfunc TestGauge(t *testing.T) {\n\tprefix, name := \"ghi.\", \"jkl\"\n\tlabel, value := \"xyz\", \"abc\"\n\tregex := `^` + prefix + name + `:([0-9\\.]+)\\|g\\|#hostname:foohost,` + label + `:` + value + `$`\n\td := New(prefix, log.NewNopLogger(), \"hostname\", \"foohost\")\n\tgauge := d.NewGauge(name).With(label, value)\n\tvaluef := teststat.LastLine(d, regex)\n\tif err := teststat.TestGauge(gauge, valuef); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\n// DogStatsD histograms just emit all observations. So, we collect them into\n// a generic histogram, and run the statistics test on that.\n\nfunc TestHistogram(t *testing.T) {\n\tprefix, name := \"dogstatsd.\", \"histogram_test\"\n\tlabel, value := \"abc\", \"def\"\n\tregex := `^` + prefix + name + `:([0-9\\.]+)\\|h\\|#` + label + `:` + value + `$`\n\td := New(prefix, log.NewNopLogger())\n\thistogram := d.NewHistogram(name, 1.0).With(label, value)\n\tquantiles := teststat.Quantiles(d, regex, 50) // no |@0.X\n\tif err := teststat.TestHistogram(histogram, quantiles, 0.01); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestHistogramSampled(t *testing.T) {\n\tprefix, name := \"dogstatsd.\", \"sampled_histogram_test\"\n\tlabel, value := \"foo\", \"bar\"\n\tregex := `^` + prefix + name + `:([0-9\\.]+)\\|h\\|@0\\.01[0]*\\|#` + label + `:` + value + `$`\n\td := New(prefix, log.NewNopLogger())\n\thistogram := d.NewHistogram(name, 0.01).With(label, value)\n\tquantiles := teststat.Quantiles(d, regex, 50)\n\tif err := teststat.TestHistogram(histogram, quantiles, 0.02); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestTiming(t *testing.T) {\n\tprefix, name := \"dogstatsd.\", \"timing_test\"\n\tlabel, value := \"wiggle\", \"bottom\"\n\tregex := `^` + prefix + name + `:([0-9\\.]+)\\|ms\\|#` + label + `:` + value + `$`\n\td := New(prefix, log.NewNopLogger())\n\thistogram := d.NewTiming(name, 1.0).With(label, value)\n\tquantiles := teststat.Quantiles(d, regex, 50) // no |@0.X\n\tif err := teststat.TestHistogram(histogram, quantiles, 0.01); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestTimingSampled(t *testing.T) {\n\tprefix, name := \"dogstatsd.\", \"sampled_timing_test\"\n\tlabel, value := \"internal\", \"external\"\n\tregex := `^` + prefix + name + `:([0-9\\.]+)\\|ms\\|@0.03[0]*\\|#` + label + `:` + value + `$`\n\td := New(prefix, log.NewNopLogger())\n\thistogram := d.NewTiming(name, 0.03).With(label, value)\n\tquantiles := teststat.Quantiles(d, regex, 50)\n\tif err := teststat.TestHistogram(histogram, quantiles, 0.02); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "metrics/expvar/expvar.go",
    "content": "// Package expvar provides expvar backends for metrics.\n// Label values are not supported.\npackage expvar\n\nimport (\n\t\"expvar\"\n\t\"sync\"\n\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/generic\"\n)\n\n// Counter implements the counter metric with an expvar float.\n// Label values are not supported.\ntype Counter struct {\n\tf *expvar.Float\n}\n\n// NewCounter creates an expvar Float with the given name, and returns an object\n// that implements the Counter interface.\nfunc NewCounter(name string) *Counter {\n\treturn &Counter{\n\t\tf: expvar.NewFloat(name),\n\t}\n}\n\n// With is a no-op.\nfunc (c *Counter) With(labelValues ...string) metrics.Counter { return c }\n\n// Add implements Counter.\nfunc (c *Counter) Add(delta float64) { c.f.Add(delta) }\n\n// Gauge implements the gauge metric with an expvar float.\n// Label values are not supported.\ntype Gauge struct {\n\tf *expvar.Float\n}\n\n// NewGauge creates an expvar Float with the given name, and returns an object\n// that implements the Gauge interface.\nfunc NewGauge(name string) *Gauge {\n\treturn &Gauge{\n\t\tf: expvar.NewFloat(name),\n\t}\n}\n\n// With is a no-op.\nfunc (g *Gauge) With(labelValues ...string) metrics.Gauge { return g }\n\n// Set implements Gauge.\nfunc (g *Gauge) Set(value float64) { g.f.Set(value) }\n\n// Add implements metrics.Gauge.\nfunc (g *Gauge) Add(delta float64) { g.f.Add(delta) }\n\n// Histogram implements the histogram metric with a combination of the generic\n// Histogram object and several expvar Floats, one for each of the 50th, 90th,\n// 95th, and 99th quantiles of observed values, with the quantile attached to\n// the name as a suffix. Label values are not supported.\ntype Histogram struct {\n\tmtx sync.Mutex\n\th   *generic.Histogram\n\tp50 *expvar.Float\n\tp90 *expvar.Float\n\tp95 *expvar.Float\n\tp99 *expvar.Float\n}\n\n// NewHistogram returns a Histogram object with the given name and number of\n// buckets in the underlying histogram object. 50 is a good default number of\n// buckets.\nfunc NewHistogram(name string, buckets int) *Histogram {\n\treturn &Histogram{\n\t\th:   generic.NewHistogram(name, buckets),\n\t\tp50: expvar.NewFloat(name + \".p50\"),\n\t\tp90: expvar.NewFloat(name + \".p90\"),\n\t\tp95: expvar.NewFloat(name + \".p95\"),\n\t\tp99: expvar.NewFloat(name + \".p99\"),\n\t}\n}\n\n// With is a no-op.\nfunc (h *Histogram) With(labelValues ...string) metrics.Histogram { return h }\n\n// Observe implements Histogram.\nfunc (h *Histogram) Observe(value float64) {\n\th.mtx.Lock()\n\tdefer h.mtx.Unlock()\n\th.h.Observe(value)\n\th.p50.Set(h.h.Quantile(0.50))\n\th.p90.Set(h.h.Quantile(0.90))\n\th.p95.Set(h.h.Quantile(0.95))\n\th.p99.Set(h.h.Quantile(0.99))\n}\n"
  },
  {
    "path": "metrics/expvar/expvar_test.go",
    "content": "package expvar\n\nimport (\n\t\"strconv\"\n\t\"testing\"\n\n\t\"github.com/go-kit/kit/metrics/teststat\"\n)\n\nfunc TestCounter(t *testing.T) {\n\tcounter := NewCounter(\"expvar_counter\").With(\"label values\", \"not supported\").(*Counter)\n\tvalue := func() float64 { f, _ := strconv.ParseFloat(counter.f.String(), 64); return f }\n\tif err := teststat.TestCounter(counter, value); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestGauge(t *testing.T) {\n\tgauge := NewGauge(\"expvar_gauge\").With(\"label values\", \"not supported\").(*Gauge)\n\tvalue := func() []float64 { f, _ := strconv.ParseFloat(gauge.f.String(), 64); return []float64{f} }\n\tif err := teststat.TestGauge(gauge, value); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestHistogram(t *testing.T) {\n\thistogram := NewHistogram(\"expvar_histogram\", 50).With(\"label values\", \"not supported\").(*Histogram)\n\tquantiles := func() (float64, float64, float64, float64) {\n\t\tp50, _ := strconv.ParseFloat(histogram.p50.String(), 64)\n\t\tp90, _ := strconv.ParseFloat(histogram.p90.String(), 64)\n\t\tp95, _ := strconv.ParseFloat(histogram.p95.String(), 64)\n\t\tp99, _ := strconv.ParseFloat(histogram.p99.String(), 64)\n\t\treturn p50, p90, p95, p99\n\t}\n\tif err := teststat.TestHistogram(histogram, quantiles, 0.01); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "metrics/generic/generic.go",
    "content": "// Package generic implements generic versions of each of the metric types. They\n// can be embedded by other implementations, and converted to specific formats\n// as necessary.\npackage generic\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"sync\"\n\t\"sync/atomic\"\n\n\t\"github.com/VividCortex/gohistogram\"\n\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/internal/lv\"\n)\n\n// Counter is an in-memory implementation of a Counter.\ntype Counter struct {\n\tbits uint64 // bits has to be the first word in order to be 64-aligned on 32-bit\n\tName string\n\tlvs  lv.LabelValues\n}\n\n// NewCounter returns a new, usable Counter.\nfunc NewCounter(name string) *Counter {\n\treturn &Counter{\n\t\tName: name,\n\t}\n}\n\n// With implements Counter.\nfunc (c *Counter) With(labelValues ...string) metrics.Counter {\n\treturn &Counter{\n\t\tName: c.Name,\n\t\tbits: atomic.LoadUint64(&c.bits),\n\t\tlvs:  c.lvs.With(labelValues...),\n\t}\n}\n\n// Add implements Counter.\nfunc (c *Counter) Add(delta float64) {\n\tfor {\n\t\tvar (\n\t\t\told  = atomic.LoadUint64(&c.bits)\n\t\t\tnewf = math.Float64frombits(old) + delta\n\t\t\tnew  = math.Float64bits(newf)\n\t\t)\n\t\tif atomic.CompareAndSwapUint64(&c.bits, old, new) {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\n// Value returns the current value of the counter.\nfunc (c *Counter) Value() float64 {\n\treturn math.Float64frombits(atomic.LoadUint64(&c.bits))\n}\n\n// ValueReset returns the current value of the counter, and resets it to zero.\n// This is useful for metrics backends whose counter aggregations expect deltas,\n// like Graphite.\nfunc (c *Counter) ValueReset() float64 {\n\tfor {\n\t\tvar (\n\t\t\told  = atomic.LoadUint64(&c.bits)\n\t\t\tnewf = 0.0\n\t\t\tnew  = math.Float64bits(newf)\n\t\t)\n\t\tif atomic.CompareAndSwapUint64(&c.bits, old, new) {\n\t\t\treturn math.Float64frombits(old)\n\t\t}\n\t}\n}\n\n// LabelValues returns the set of label values attached to the counter.\nfunc (c *Counter) LabelValues() []string {\n\treturn c.lvs\n}\n\n// Gauge is an in-memory implementation of a Gauge.\ntype Gauge struct {\n\tbits uint64 // bits has to be the first word in order to be 64-aligned on 32-bit\n\tName string\n\tlvs  lv.LabelValues\n}\n\n// NewGauge returns a new, usable Gauge.\nfunc NewGauge(name string) *Gauge {\n\treturn &Gauge{\n\t\tName: name,\n\t}\n}\n\n// With implements Gauge.\nfunc (g *Gauge) With(labelValues ...string) metrics.Gauge {\n\treturn &Gauge{\n\t\tName: g.Name,\n\t\tbits: atomic.LoadUint64(&g.bits),\n\t\tlvs:  g.lvs.With(labelValues...),\n\t}\n}\n\n// Set implements Gauge.\nfunc (g *Gauge) Set(value float64) {\n\tatomic.StoreUint64(&g.bits, math.Float64bits(value))\n}\n\n// Add implements metrics.Gauge.\nfunc (g *Gauge) Add(delta float64) {\n\tfor {\n\t\tvar (\n\t\t\told  = atomic.LoadUint64(&g.bits)\n\t\t\tnewf = math.Float64frombits(old) + delta\n\t\t\tnew  = math.Float64bits(newf)\n\t\t)\n\t\tif atomic.CompareAndSwapUint64(&g.bits, old, new) {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\n// Value returns the current value of the gauge.\nfunc (g *Gauge) Value() float64 {\n\treturn math.Float64frombits(atomic.LoadUint64(&g.bits))\n}\n\n// LabelValues returns the set of label values attached to the gauge.\nfunc (g *Gauge) LabelValues() []string {\n\treturn g.lvs\n}\n\n// Histogram is an in-memory implementation of a streaming histogram, based on\n// VividCortex/gohistogram. It dynamically computes quantiles, so it's not\n// suitable for aggregation.\ntype Histogram struct {\n\tName string\n\tlvs  lv.LabelValues\n\th    *safeHistogram\n}\n\n// NewHistogram returns a numeric histogram based on VividCortex/gohistogram. A\n// good default value for buckets is 50.\nfunc NewHistogram(name string, buckets int) *Histogram {\n\treturn &Histogram{\n\t\tName: name,\n\t\th:    &safeHistogram{Histogram: gohistogram.NewHistogram(buckets)},\n\t}\n}\n\n// With implements Histogram.\nfunc (h *Histogram) With(labelValues ...string) metrics.Histogram {\n\treturn &Histogram{\n\t\tName: h.Name,\n\t\tlvs:  h.lvs.With(labelValues...),\n\t\th:    h.h,\n\t}\n}\n\n// Observe implements Histogram.\nfunc (h *Histogram) Observe(value float64) {\n\th.h.Lock()\n\tdefer h.h.Unlock()\n\th.h.Add(value)\n}\n\n// Quantile returns the value of the quantile q, 0.0 < q < 1.0.\nfunc (h *Histogram) Quantile(q float64) float64 {\n\th.h.RLock()\n\tdefer h.h.RUnlock()\n\treturn h.h.Quantile(q)\n}\n\n// LabelValues returns the set of label values attached to the histogram.\nfunc (h *Histogram) LabelValues() []string {\n\treturn h.lvs\n}\n\n// Print writes a string representation of the histogram to the passed writer.\n// Useful for printing to a terminal.\nfunc (h *Histogram) Print(w io.Writer) {\n\th.h.RLock()\n\tdefer h.h.RUnlock()\n\tfmt.Fprint(w, h.h.String())\n}\n\n// safeHistogram exists as gohistogram.Histogram is not goroutine-safe.\ntype safeHistogram struct {\n\tsync.RWMutex\n\tgohistogram.Histogram\n}\n\n// Bucket is a range in a histogram which aggregates observations.\ntype Bucket struct {\n\tFrom, To, Count int64\n}\n\n// Quantile is a pair of a quantile (0..100) and its observed maximum value.\ntype Quantile struct {\n\tQuantile int // 0..100\n\tValue    int64\n}\n\n// SimpleHistogram is an in-memory implementation of a Histogram. It only tracks\n// an approximate moving average, so is likely too naïve for many use cases.\ntype SimpleHistogram struct {\n\tmtx sync.RWMutex\n\tlvs lv.LabelValues\n\tavg float64\n\tn   uint64\n}\n\n// NewSimpleHistogram returns a SimpleHistogram, ready for observations.\nfunc NewSimpleHistogram() *SimpleHistogram {\n\treturn &SimpleHistogram{}\n}\n\n// With implements Histogram.\nfunc (h *SimpleHistogram) With(labelValues ...string) metrics.Histogram {\n\treturn &SimpleHistogram{\n\t\tlvs: h.lvs.With(labelValues...),\n\t\tavg: h.avg,\n\t\tn:   h.n,\n\t}\n}\n\n// Observe implements Histogram.\nfunc (h *SimpleHistogram) Observe(value float64) {\n\th.mtx.Lock()\n\tdefer h.mtx.Unlock()\n\th.n++\n\th.avg -= h.avg / float64(h.n)\n\th.avg += value / float64(h.n)\n}\n\n// ApproximateMovingAverage returns the approximate moving average of observations.\nfunc (h *SimpleHistogram) ApproximateMovingAverage() float64 {\n\th.mtx.RLock()\n\tdefer h.mtx.RUnlock()\n\treturn h.avg\n}\n\n// LabelValues returns the set of label values attached to the histogram.\nfunc (h *SimpleHistogram) LabelValues() []string {\n\treturn h.lvs\n}\n"
  },
  {
    "path": "metrics/generic/generic_test.go",
    "content": "package generic_test\n\n// This is package generic_test in order to get around an import cycle: this\n// package imports teststat to do its testing, but package teststat imports\n// generic to use its Histogram in the Quantiles helper function.\n\nimport (\n\t\"go/ast\"\n\t\"go/importer\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"io/ioutil\"\n\t\"math\"\n\t\"math/rand\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/go-kit/kit/metrics/generic\"\n\t\"github.com/go-kit/kit/metrics/teststat\"\n)\n\nfunc TestCounter(t *testing.T) {\n\tname := \"my_counter\"\n\tcounter := generic.NewCounter(name).With(\"label\", \"counter\").(*generic.Counter)\n\tif want, have := name, counter.Name; want != have {\n\t\tt.Errorf(\"Name: want %q, have %q\", want, have)\n\t}\n\tvalue := counter.Value\n\tif err := teststat.TestCounter(counter, value); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestValueReset(t *testing.T) {\n\tcounter := generic.NewCounter(\"test_value_reset\")\n\tcounter.Add(123)\n\tcounter.Add(456)\n\tcounter.Add(789)\n\tif want, have := float64(123+456+789), counter.ValueReset(); want != have {\n\t\tt.Errorf(\"want %f, have %f\", want, have)\n\t}\n\tif want, have := float64(0), counter.Value(); want != have {\n\t\tt.Errorf(\"want %f, have %f\", want, have)\n\t}\n}\n\nfunc TestGauge(t *testing.T) {\n\tname := \"my_gauge\"\n\tgauge := generic.NewGauge(name).With(\"label\", \"gauge\").(*generic.Gauge)\n\tif want, have := name, gauge.Name; want != have {\n\t\tt.Errorf(\"Name: want %q, have %q\", want, have)\n\t}\n\tvalue := func() []float64 { return []float64{gauge.Value()} }\n\tif err := teststat.TestGauge(gauge, value); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestHistogram(t *testing.T) {\n\tname := \"my_histogram\"\n\thistogram := generic.NewHistogram(name, 50).With(\"label\", \"histogram\").(*generic.Histogram)\n\tif want, have := name, histogram.Name; want != have {\n\t\tt.Errorf(\"Name: want %q, have %q\", want, have)\n\t}\n\tquantiles := func() (float64, float64, float64, float64) {\n\t\treturn histogram.Quantile(0.50), histogram.Quantile(0.90), histogram.Quantile(0.95), histogram.Quantile(0.99)\n\t}\n\tif err := teststat.TestHistogram(histogram, quantiles, 0.01); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestIssue424(t *testing.T) {\n\tvar (\n\t\thistogram   = generic.NewHistogram(\"dont_panic\", 50)\n\t\tconcurrency = 100\n\t\toperations  = 1000\n\t\twg          sync.WaitGroup\n\t)\n\n\twg.Add(concurrency)\n\tfor i := 0; i < concurrency; i++ {\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tfor j := 0; j < operations; j++ {\n\t\t\t\thistogram.Observe(float64(j))\n\t\t\t\thistogram.Observe(histogram.Quantile(0.5))\n\t\t\t}\n\t\t}()\n\t}\n\twg.Wait()\n}\n\nfunc TestSimpleHistogram(t *testing.T) {\n\thistogram := generic.NewSimpleHistogram().With(\"label\", \"simple_histogram\").(*generic.SimpleHistogram)\n\tvar (\n\t\tsum   int\n\t\tcount = 1234 // not too big\n\t)\n\tfor i := 0; i < count; i++ {\n\t\tvalue := rand.Intn(1000)\n\t\tsum += value\n\t\thistogram.Observe(float64(value))\n\t}\n\n\tvar (\n\t\twant      = float64(sum) / float64(count)\n\t\thave      = histogram.ApproximateMovingAverage()\n\t\ttolerance = 0.001 // real real slim\n\t)\n\tif math.Abs(want-have)/want > tolerance {\n\t\tt.Errorf(\"want %f, have %f\", want, have)\n\t}\n}\n\n// Naive atomic alignment test.\n// The problem is related to the use of `atomic.*` and not directly to a structure.\n// But currently works for Counter and Gauge.\n// To have a more solid test, this test should be removed and the other tests should be run on a 32-bit arch.\nfunc TestAtomicAlignment(t *testing.T) {\n\tcontent, err := ioutil.ReadFile(\"./generic.go\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tfset := token.NewFileSet()\n\n\tfile, err := parser.ParseFile(fset, \"generic.go\", content, parser.ParseComments)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tconf := types.Config{Importer: importer.ForCompiler(fset, \"source\", nil)}\n\n\tpkg, err := conf.Check(\".\", fset, []*ast.File{file}, nil)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// uses ARM as reference for 32-bit arch\n\tsizes := types.SizesFor(\"gc\", \"arm\")\n\n\tnames := []string{\"Counter\", \"Gauge\"}\n\n\tfor _, name := range names {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tcheckAtomicAlignment(t, sizes, pkg.Scope().Lookup(name), pkg)\n\t\t})\n\t}\n}\n\nfunc checkAtomicAlignment(t *testing.T, sizes types.Sizes, obj types.Object, pkg *types.Package) {\n\tt.Helper()\n\n\tst := obj.Type().Underlying().(*types.Struct)\n\n\tposToCheck := make(map[int]types.Type)\n\n\tvar vars []*types.Var\n\tfor i := 0; i < st.NumFields(); i++ {\n\t\tfield := st.Field(i)\n\n\t\tif v, ok := field.Type().(*types.Basic); ok {\n\t\t\tswitch v.Kind() {\n\t\t\tcase types.Uint64, types.Float64, types.Int64:\n\t\t\t\tposToCheck[i] = v\n\t\t\t}\n\t\t}\n\n\t\tvars = append(vars, types.NewVar(field.Pos(), pkg, field.Name(), field.Type()))\n\t}\n\n\toffsets := sizes.Offsetsof(vars)\n\tfor i, offset := range offsets {\n\t\tif _, ok := posToCheck[i]; !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tif offset%8 != 0 {\n\t\t\tt.Errorf(\"misalignment detected in %s for the type %s, offset %d\", obj.Name(), posToCheck[i], offset)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "metrics/graphite/graphite.go",
    "content": "// Package graphite provides a Graphite backend for metrics. Metrics are batched\n// and emitted in the plaintext protocol. For more information, see\n// http://graphite.readthedocs.io/en/latest/feeding-carbon.html#the-plaintext-protocol\n//\n// Graphite does not have a native understanding of metric parameterization, so\n// label values not supported. Use distinct metrics for each unique combination\n// of label values.\npackage graphite\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/generic\"\n\t\"github.com/go-kit/kit/util/conn\"\n\t\"github.com/go-kit/log\"\n)\n\n// Graphite receives metrics observations and forwards them to a Graphite server.\n// Create a Graphite object, use it to create metrics, and pass those metrics as\n// dependencies to the components that will use them.\n//\n// All metrics are buffered until WriteTo is called. Counters and gauges are\n// aggregated into a single observation per timeseries per write. Histograms are\n// exploded into per-quantile gauges and reported once per write.\n//\n// To regularly report metrics to an io.Writer, use the WriteLoop helper method.\n// To send to a Graphite server, use the SendLoop helper method.\ntype Graphite struct {\n\tmtx        sync.RWMutex\n\tprefix     string\n\tcounters   map[string]*Counter\n\tgauges     map[string]*Gauge\n\thistograms map[string]*Histogram\n\tlogger     log.Logger\n}\n\n// New returns a Graphite object that may be used to create metrics. Prefix is\n// applied to all created metrics. Callers must ensure that regular calls to\n// WriteTo are performed, either manually or with one of the helper methods.\nfunc New(prefix string, logger log.Logger) *Graphite {\n\treturn &Graphite{\n\t\tprefix:     prefix,\n\t\tcounters:   map[string]*Counter{},\n\t\tgauges:     map[string]*Gauge{},\n\t\thistograms: map[string]*Histogram{},\n\t\tlogger:     logger,\n\t}\n}\n\n// NewCounter returns a counter. Observations are aggregated and emitted once\n// per write invocation.\nfunc (g *Graphite) NewCounter(name string) *Counter {\n\tc := NewCounter(g.prefix + name)\n\tg.mtx.Lock()\n\tg.counters[g.prefix+name] = c\n\tg.mtx.Unlock()\n\treturn c\n}\n\n// NewGauge returns a gauge. Observations are aggregated and emitted once per\n// write invocation.\nfunc (g *Graphite) NewGauge(name string) *Gauge {\n\tga := NewGauge(g.prefix + name)\n\tg.mtx.Lock()\n\tg.gauges[g.prefix+name] = ga\n\tg.mtx.Unlock()\n\treturn ga\n}\n\n// NewHistogram returns a histogram. Observations are aggregated and emitted as\n// per-quantile gauges, once per write invocation. 50 is a good default value\n// for buckets.\nfunc (g *Graphite) NewHistogram(name string, buckets int) *Histogram {\n\th := NewHistogram(g.prefix+name, buckets)\n\tg.mtx.Lock()\n\tg.histograms[g.prefix+name] = h\n\tg.mtx.Unlock()\n\treturn h\n}\n\n// WriteLoop is a helper method that invokes WriteTo to the passed writer every\n// time the passed channel fires. This method blocks until ctx is canceled,\n// so clients probably want to run it in its own goroutine. For typical\n// usage, create a time.Ticker and pass its C channel to this method.\nfunc (g *Graphite) WriteLoop(ctx context.Context, c <-chan time.Time, w io.Writer) {\n\tfor {\n\t\tselect {\n\t\tcase <-c:\n\t\t\tif _, err := g.WriteTo(w); err != nil {\n\t\t\t\tg.logger.Log(\"during\", \"WriteTo\", \"err\", err)\n\t\t\t}\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// SendLoop is a helper method that wraps WriteLoop, passing a managed\n// connection to the network and address. Like WriteLoop, this method blocks\n// until ctx is canceled, so clients probably want to start it in its own\n// goroutine. For typical usage, create a time.Ticker and pass its C channel to\n// this method.\nfunc (g *Graphite) SendLoop(ctx context.Context, c <-chan time.Time, network, address string) {\n\tg.WriteLoop(ctx, c, conn.NewDefaultManager(network, address, g.logger))\n}\n\n// WriteTo flushes the buffered content of the metrics to the writer, in\n// Graphite plaintext format. WriteTo abides best-effort semantics, so\n// observations are lost if there is a problem with the write. Clients should be\n// sure to call WriteTo regularly, ideally through the WriteLoop or SendLoop\n// helper methods.\nfunc (g *Graphite) WriteTo(w io.Writer) (count int64, err error) {\n\tg.mtx.RLock()\n\tdefer g.mtx.RUnlock()\n\tnow := time.Now().Unix()\n\n\tfor name, c := range g.counters {\n\t\tn, err := fmt.Fprintf(w, \"%s %f %d\\n\", name, c.c.ValueReset(), now)\n\t\tif err != nil {\n\t\t\treturn count, err\n\t\t}\n\t\tcount += int64(n)\n\t}\n\n\tfor name, ga := range g.gauges {\n\t\tn, err := fmt.Fprintf(w, \"%s %f %d\\n\", name, ga.g.Value(), now)\n\t\tif err != nil {\n\t\t\treturn count, err\n\t\t}\n\t\tcount += int64(n)\n\t}\n\n\tfor name, h := range g.histograms {\n\t\tfor _, p := range []struct {\n\t\t\ts string\n\t\t\tf float64\n\t\t}{\n\t\t\t{\"50\", 0.50},\n\t\t\t{\"90\", 0.90},\n\t\t\t{\"95\", 0.95},\n\t\t\t{\"99\", 0.99},\n\t\t} {\n\t\t\tn, err := fmt.Fprintf(w, \"%s.p%s %f %d\\n\", name, p.s, h.h.Quantile(p.f), now)\n\t\t\tif err != nil {\n\t\t\t\treturn count, err\n\t\t\t}\n\t\t\tcount += int64(n)\n\t\t}\n\t}\n\n\treturn count, err\n}\n\n// Counter is a Graphite counter metric.\ntype Counter struct {\n\tc *generic.Counter\n}\n\n// NewCounter returns a new usable counter metric.\nfunc NewCounter(name string) *Counter {\n\treturn &Counter{generic.NewCounter(name)}\n}\n\n// With is a no-op.\nfunc (c *Counter) With(...string) metrics.Counter { return c }\n\n// Add implements counter.\nfunc (c *Counter) Add(delta float64) { c.c.Add(delta) }\n\n// Gauge is a Graphite gauge metric.\ntype Gauge struct {\n\tg *generic.Gauge\n}\n\n// NewGauge returns a new usable Gauge metric.\nfunc NewGauge(name string) *Gauge {\n\treturn &Gauge{generic.NewGauge(name)}\n}\n\n// With is a no-op.\nfunc (g *Gauge) With(...string) metrics.Gauge { return g }\n\n// Set implements gauge.\nfunc (g *Gauge) Set(value float64) { g.g.Set(value) }\n\n// Add implements metrics.Gauge.\nfunc (g *Gauge) Add(delta float64) { g.g.Add(delta) }\n\n// Histogram is a Graphite histogram metric. Observations are bucketed into\n// per-quantile gauges.\ntype Histogram struct {\n\th *generic.Histogram\n}\n\n// NewHistogram returns a new usable Histogram metric.\nfunc NewHistogram(name string, buckets int) *Histogram {\n\treturn &Histogram{generic.NewHistogram(name, buckets)}\n}\n\n// With is a no-op.\nfunc (h *Histogram) With(...string) metrics.Histogram { return h }\n\n// Observe implements histogram.\nfunc (h *Histogram) Observe(value float64) { h.h.Observe(value) }\n"
  },
  {
    "path": "metrics/graphite/graphite_test.go",
    "content": "package graphite\n\nimport (\n\t\"bytes\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"testing\"\n\n\t\"github.com/go-kit/kit/metrics/teststat\"\n\t\"github.com/go-kit/log\"\n)\n\nfunc TestCounter(t *testing.T) {\n\tprefix, name := \"abc.\", \"def\"\n\tlabel, value := \"label\", \"value\" // ignored for Graphite\n\tregex := `^` + prefix + name + ` ([0-9\\.]+) [0-9]+$`\n\tg := New(prefix, log.NewNopLogger())\n\tcounter := g.NewCounter(name).With(label, value)\n\tvaluef := teststat.SumLines(g, regex)\n\tif err := teststat.TestCounter(counter, valuef); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestGauge(t *testing.T) {\n\tprefix, name := \"ghi.\", \"jkl\"\n\tlabel, value := \"xyz\", \"abc\" // ignored for Graphite\n\tregex := `^` + prefix + name + ` ([0-9\\.]+) [0-9]+$`\n\tg := New(prefix, log.NewNopLogger())\n\tgauge := g.NewGauge(name).With(label, value)\n\tvaluef := teststat.LastLine(g, regex)\n\tif err := teststat.TestGauge(gauge, valuef); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestHistogram(t *testing.T) {\n\t// The histogram test is actually like 4 gauge tests.\n\tprefix, name := \"graphite.\", \"histogram_test\"\n\tlabel, value := \"abc\", \"def\" // ignored for Graphite\n\tre50 := regexp.MustCompile(prefix + name + `.p50 ([0-9\\.]+) [0-9]+`)\n\tre90 := regexp.MustCompile(prefix + name + `.p90 ([0-9\\.]+) [0-9]+`)\n\tre95 := regexp.MustCompile(prefix + name + `.p95 ([0-9\\.]+) [0-9]+`)\n\tre99 := regexp.MustCompile(prefix + name + `.p99 ([0-9\\.]+) [0-9]+`)\n\tg := New(prefix, log.NewNopLogger())\n\thistogram := g.NewHistogram(name, 50).With(label, value)\n\tquantiles := func() (float64, float64, float64, float64) {\n\t\tvar buf bytes.Buffer\n\t\tg.WriteTo(&buf)\n\t\tmatch50 := re50.FindStringSubmatch(buf.String())\n\t\tp50, _ := strconv.ParseFloat(match50[1], 64)\n\t\tmatch90 := re90.FindStringSubmatch(buf.String())\n\t\tp90, _ := strconv.ParseFloat(match90[1], 64)\n\t\tmatch95 := re95.FindStringSubmatch(buf.String())\n\t\tp95, _ := strconv.ParseFloat(match95[1], 64)\n\t\tmatch99 := re99.FindStringSubmatch(buf.String())\n\t\tp99, _ := strconv.ParseFloat(match99[1], 64)\n\t\treturn p50, p90, p95, p99\n\t}\n\tif err := teststat.TestHistogram(histogram, quantiles, 0.01); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "metrics/influx/example_test.go",
    "content": "package influx\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n\n\tinfluxdb \"github.com/influxdata/influxdb1-client/v2\"\n\n\t\"github.com/go-kit/log\"\n)\n\nfunc ExampleCounter() {\n\tin := New(map[string]string{\"a\": \"b\"}, influxdb.BatchPointsConfig{}, log.NewNopLogger())\n\tcounter := in.NewCounter(\"influx_counter\")\n\tcounter.Add(10)\n\tcounter.With(\"error\", \"true\").Add(1)\n\tcounter.With(\"error\", \"false\").Add(2)\n\tcounter.Add(50)\n\n\tclient := &bufWriter{}\n\tin.WriteTo(client)\n\n\texpectedLines := []string{\n\t\t`(influx_counter,a=b count=60) [0-9]{19}`,\n\t\t`(influx_counter,a=b,error=true count=1) [0-9]{19}`,\n\t\t`(influx_counter,a=b,error=false count=2) [0-9]{19}`,\n\t}\n\n\tif err := extractAndPrintMessage(expectedLines, client.buf.String()); err != nil {\n\t\tfmt.Println(err.Error())\n\t}\n\n\t// Output:\n\t// influx_counter,a=b count=60\n\t// influx_counter,a=b,error=true count=1\n\t// influx_counter,a=b,error=false count=2\n}\n\nfunc ExampleGauge() {\n\tin := New(map[string]string{\"a\": \"b\"}, influxdb.BatchPointsConfig{}, log.NewNopLogger())\n\tgauge := in.NewGauge(\"influx_gauge\")\n\tgauge.Set(10)\n\tgauge.With(\"error\", \"true\").Set(2)\n\tgauge.With(\"error\", \"true\").Set(1)\n\tgauge.With(\"error\", \"false\").Set(2)\n\tgauge.Set(50)\n\tgauge.With(\"test\", \"true\").Set(1)\n\tgauge.With(\"test\", \"true\").Add(1)\n\n\tclient := &bufWriter{}\n\tin.WriteTo(client)\n\n\texpectedLines := []string{\n\t\t`(influx_gauge,a=b,test=true value=2) [0-9]{19}`,\n\t\t`(influx_gauge,a=b value=50) [0-9]{19}`,\n\t\t`(influx_gauge,a=b,error=true value=1) [0-9]{19}`,\n\t\t`(influx_gauge,a=b,error=false value=2) [0-9]{19}`,\n\t}\n\n\tif err := extractAndPrintMessage(expectedLines, client.buf.String()); err != nil {\n\t\tfmt.Println(err.Error())\n\t}\n\n\t// Output:\n\t// influx_gauge,a=b,test=true value=2\n\t// influx_gauge,a=b value=50\n\t// influx_gauge,a=b,error=true value=1\n\t// influx_gauge,a=b,error=false value=2\n}\n\nfunc ExampleHistogram() {\n\tin := New(map[string]string{\"foo\": \"alpha\"}, influxdb.BatchPointsConfig{}, log.NewNopLogger())\n\thistogram := in.NewHistogram(\"influx_histogram\")\n\thistogram.Observe(float64(10))\n\thistogram.With(\"error\", \"true\").Observe(float64(1))\n\thistogram.With(\"error\", \"false\").Observe(float64(2))\n\thistogram.Observe(float64(50))\n\n\tclient := &bufWriter{}\n\tin.WriteTo(client)\n\n\texpectedLines := []string{\n\t\t`(influx_histogram,foo=alpha p50=10,p90=50,p95=50,p99=50) [0-9]{19}`,\n\t\t`(influx_histogram,error=true,foo=alpha p50=1,p90=1,p95=1,p99=1) [0-9]{19}`,\n\t\t`(influx_histogram,error=false,foo=alpha p50=2,p90=2,p95=2,p99=2) [0-9]{19}`,\n\t}\n\n\tif err := extractAndPrintMessage(expectedLines, client.buf.String()); err != nil {\n\t\tfmt.Println(err.Error())\n\t}\n\n\t// Output:\n\t// influx_histogram,foo=alpha p50=10,p90=50,p95=50,p99=50\n\t// influx_histogram,error=true,foo=alpha p50=1,p90=1,p95=1,p99=1\n\t// influx_histogram,error=false,foo=alpha p50=2,p90=2,p95=2,p99=2\n}\n\nfunc extractAndPrintMessage(expected []string, msg string) error {\n\tfor _, pattern := range expected {\n\t\tre := regexp.MustCompile(pattern)\n\t\tmatch := re.FindStringSubmatch(msg)\n\t\tif len(match) != 2 {\n\t\t\treturn fmt.Errorf(\"pattern not found! {%s} [%s]: %v\\n\", pattern, msg, match)\n\t\t}\n\t\tfmt.Println(match[1])\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "metrics/influx/influx.go",
    "content": "// Package influx provides an InfluxDB implementation for metrics. The model is\n// similar to other push-based instrumentation systems. Observations are\n// aggregated locally and emitted to the Influx server on regular intervals.\npackage influx\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\tinfluxdb \"github.com/influxdata/influxdb1-client/v2\"\n\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/generic\"\n\t\"github.com/go-kit/kit/metrics/internal/lv\"\n\t\"github.com/go-kit/log\"\n)\n\n// Influx is a store for metrics that will be emitted to an Influx database.\n//\n// Influx is a general purpose time-series database, and has no native concepts\n// of counters, gauges, or histograms. Counters are modeled as a timeseries with\n// one data point per flush, with a \"count\" field that reflects all adds since\n// the last flush. Gauges are modeled as a timeseries with one data point per\n// flush, with a \"value\" field that reflects the current state of the gauge.\n// Histograms are modeled as a timeseries with one data point per combination of tags,\n// with a set of quantile fields that reflects the p50, p90, p95 & p99.\n//\n// Influx tags are attached to the Influx object, can be given to each\n// metric at construction and can be updated anytime via With function. Influx fields\n// are mapped to Go kit label values directly by this collector. Actual metric\n// values are provided as fields with specific names depending on the metric.\n//\n// All observations are collected in memory locally, and flushed on demand.\ntype Influx struct {\n\tcounters   *lv.Space\n\tgauges     *lv.Space\n\thistograms *lv.Space\n\ttags       map[string]string\n\tconf       influxdb.BatchPointsConfig\n\tlogger     log.Logger\n}\n\n// New returns an Influx, ready to create metrics and collect observations. Tags\n// are applied to all metrics created from this object. The BatchPointsConfig is\n// used during flushing.\nfunc New(tags map[string]string, conf influxdb.BatchPointsConfig, logger log.Logger) *Influx {\n\treturn &Influx{\n\t\tcounters:   lv.NewSpace(),\n\t\tgauges:     lv.NewSpace(),\n\t\thistograms: lv.NewSpace(),\n\t\ttags:       tags,\n\t\tconf:       conf,\n\t\tlogger:     logger,\n\t}\n}\n\n// NewCounter returns an Influx counter.\nfunc (in *Influx) NewCounter(name string) *Counter {\n\treturn &Counter{\n\t\tname: name,\n\t\tobs:  in.counters.Observe,\n\t}\n}\n\n// NewGauge returns an Influx gauge.\nfunc (in *Influx) NewGauge(name string) *Gauge {\n\treturn &Gauge{\n\t\tname: name,\n\t\tobs:  in.gauges.Observe,\n\t\tadd:  in.gauges.Add,\n\t}\n}\n\n// NewHistogram returns an Influx histogram.\nfunc (in *Influx) NewHistogram(name string) *Histogram {\n\treturn &Histogram{\n\t\tname: name,\n\t\tobs:  in.histograms.Observe,\n\t}\n}\n\n// BatchPointsWriter captures a subset of the influxdb.Client methods necessary\n// for emitting metrics observations.\ntype BatchPointsWriter interface {\n\tWrite(influxdb.BatchPoints) error\n}\n\n// WriteLoop is a helper method that invokes WriteTo to the passed writer every\n// time the passed channel fires. This method blocks until the channel is\n// closed, so clients probably want to run it in its own goroutine. For typical\n// usage, create a time.Ticker and pass its C channel to this method.\nfunc (in *Influx) WriteLoop(ctx context.Context, c <-chan time.Time, w BatchPointsWriter) {\n\tfor {\n\t\tselect {\n\t\tcase <-c:\n\t\t\tif err := in.WriteTo(w); err != nil {\n\t\t\t\tin.logger.Log(\"during\", \"WriteTo\", \"err\", err)\n\t\t\t}\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// WriteTo flushes the buffered content of the metrics to the writer, in an\n// Influx BatchPoints format. WriteTo abides best-effort semantics, so\n// observations are lost if there is a problem with the write. Clients should be\n// sure to call WriteTo regularly, ideally through the WriteLoop helper method.\nfunc (in *Influx) WriteTo(w BatchPointsWriter) (err error) {\n\tbp, err := influxdb.NewBatchPoints(in.conf)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tnow := time.Now()\n\n\tin.counters.Reset().Walk(func(name string, lvs lv.LabelValues, values []float64) bool {\n\t\ttags := mergeTags(in.tags, lvs)\n\t\tvar p *influxdb.Point\n\t\tfields := map[string]interface{}{\"count\": sum(values)}\n\t\tp, err = influxdb.NewPoint(name, tags, fields, now)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tbp.AddPoint(p)\n\t\treturn true\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tin.gauges.Reset().Walk(func(name string, lvs lv.LabelValues, values []float64) bool {\n\t\ttags := mergeTags(in.tags, lvs)\n\t\tvar p *influxdb.Point\n\t\tfields := map[string]interface{}{\"value\": last(values)}\n\t\tp, err = influxdb.NewPoint(name, tags, fields, now)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tbp.AddPoint(p)\n\t\treturn true\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tin.histograms.Reset().Walk(func(name string, lvs lv.LabelValues, values []float64) bool {\n\t\thistogram := generic.NewHistogram(name, 50)\n\t\ttags := mergeTags(in.tags, lvs)\n\t\tvar p *influxdb.Point\n\t\tfor _, v := range values {\n\t\t\thistogram.Observe(v)\n\t\t}\n\t\tfields := map[string]interface{}{\n\t\t\t\"p50\": histogram.Quantile(0.50),\n\t\t\t\"p90\": histogram.Quantile(0.90),\n\t\t\t\"p95\": histogram.Quantile(0.95),\n\t\t\t\"p99\": histogram.Quantile(0.99),\n\t\t}\n\t\tp, err = influxdb.NewPoint(name, tags, fields, now)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tbp.AddPoint(p)\n\t\treturn true\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn w.Write(bp)\n}\n\nfunc mergeTags(tags map[string]string, labelValues []string) map[string]string {\n\tif len(labelValues)%2 != 0 {\n\t\tpanic(\"mergeTags received a labelValues with an odd number of strings\")\n\t}\n\tret := make(map[string]string, len(tags)+len(labelValues)/2)\n\tfor k, v := range tags {\n\t\tret[k] = v\n\t}\n\tfor i := 0; i < len(labelValues); i += 2 {\n\t\tret[labelValues[i]] = labelValues[i+1]\n\t}\n\treturn ret\n}\n\nfunc sum(a []float64) float64 {\n\tvar v float64\n\tfor _, f := range a {\n\t\tv += f\n\t}\n\treturn v\n}\n\nfunc last(a []float64) float64 {\n\treturn a[len(a)-1]\n}\n\ntype observeFunc func(name string, lvs lv.LabelValues, value float64)\n\n// Counter is an Influx counter. Observations are forwarded to an Influx\n// object, and aggregated (summed) per timeseries.\ntype Counter struct {\n\tname string\n\tlvs  lv.LabelValues\n\tobs  observeFunc\n}\n\n// With implements metrics.Counter.\nfunc (c *Counter) With(labelValues ...string) metrics.Counter {\n\treturn &Counter{\n\t\tname: c.name,\n\t\tlvs:  c.lvs.With(labelValues...),\n\t\tobs:  c.obs,\n\t}\n}\n\n// Add implements metrics.Counter.\nfunc (c *Counter) Add(delta float64) {\n\tc.obs(c.name, c.lvs, delta)\n}\n\n// Gauge is an Influx gauge. Observations are forwarded to a Dogstatsd\n// object, and aggregated (the last observation selected) per timeseries.\ntype Gauge struct {\n\tname string\n\tlvs  lv.LabelValues\n\tobs  observeFunc\n\tadd  observeFunc\n}\n\n// With implements metrics.Gauge.\nfunc (g *Gauge) With(labelValues ...string) metrics.Gauge {\n\treturn &Gauge{\n\t\tname: g.name,\n\t\tlvs:  g.lvs.With(labelValues...),\n\t\tobs:  g.obs,\n\t\tadd:  g.add,\n\t}\n}\n\n// Set implements metrics.Gauge.\nfunc (g *Gauge) Set(value float64) {\n\tg.obs(g.name, g.lvs, value)\n}\n\n// Add implements metrics.Gauge.\nfunc (g *Gauge) Add(delta float64) {\n\tg.add(g.name, g.lvs, delta)\n}\n\n// Histogram is an Influx histrogram. Observations are aggregated into a\n// generic.Histogram and emitted as per-quantile gauges to the Influx server.\ntype Histogram struct {\n\tname string\n\tlvs  lv.LabelValues\n\tobs  observeFunc\n}\n\n// With implements metrics.Histogram.\nfunc (h *Histogram) With(labelValues ...string) metrics.Histogram {\n\treturn &Histogram{\n\t\tname: h.name,\n\t\tlvs:  h.lvs.With(labelValues...),\n\t\tobs:  h.obs,\n\t}\n}\n\n// Observe implements metrics.Histogram.\nfunc (h *Histogram) Observe(value float64) {\n\th.obs(h.name, h.lvs, value)\n}\n"
  },
  {
    "path": "metrics/influx/influx_test.go",
    "content": "package influx\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\n\tinfluxdb \"github.com/influxdata/influxdb1-client/v2\"\n\n\t\"github.com/go-kit/kit/metrics/teststat\"\n\t\"github.com/go-kit/log\"\n)\n\nfunc TestCounter(t *testing.T) {\n\tin := New(map[string]string{\"a\": \"b\"}, influxdb.BatchPointsConfig{}, log.NewNopLogger())\n\tre := regexp.MustCompile(`influx_counter,a=b count=([0-9\\.]+) [0-9]+`) // reverse-engineered :\\\n\tcounter := in.NewCounter(\"influx_counter\")\n\tvalue := func() float64 {\n\t\tclient := &bufWriter{}\n\t\tin.WriteTo(client)\n\t\tmatch := re.FindStringSubmatch(client.buf.String())\n\t\tf, _ := strconv.ParseFloat(match[1], 64)\n\t\treturn f\n\t}\n\tif err := teststat.TestCounter(counter, value); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestGauge(t *testing.T) {\n\tin := New(map[string]string{\"foo\": \"alpha\"}, influxdb.BatchPointsConfig{}, log.NewNopLogger())\n\tre := regexp.MustCompile(`influx_gauge,foo=alpha value=([0-9\\.]+) [0-9]+`)\n\tgauge := in.NewGauge(\"influx_gauge\")\n\tvalue := func() []float64 {\n\t\tclient := &bufWriter{}\n\t\tin.WriteTo(client)\n\t\tmatch := re.FindStringSubmatch(client.buf.String())\n\t\tf, _ := strconv.ParseFloat(match[1], 64)\n\t\treturn []float64{f}\n\t}\n\tif err := teststat.TestGauge(gauge, value); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestHistogram(t *testing.T) {\n\tin := New(map[string]string{\"foo\": \"alpha\"}, influxdb.BatchPointsConfig{}, log.NewNopLogger())\n\tre := regexp.MustCompile(`influx_histogram,bar=beta,foo=alpha p50=([0-9\\.]+),p90=([0-9\\.]+),p95=([0-9\\.]+),p99=([0-9\\.]+) [0-9]+`)\n\thistogram := in.NewHistogram(\"influx_histogram\").With(\"bar\", \"beta\")\n\tquantiles := func() (float64, float64, float64, float64) {\n\t\tw := &bufWriter{}\n\t\tin.WriteTo(w)\n\t\tmatch := re.FindStringSubmatch(w.buf.String())\n\t\tif len(match) != 5 {\n\t\t\tt.Errorf(\"These are not the quantiles you're looking for: %v\\n\", match)\n\t\t}\n\t\tvar result [4]float64\n\t\tfor i, q := range match[1:] {\n\t\t\tresult[i], _ = strconv.ParseFloat(q, 64)\n\t\t}\n\t\treturn result[0], result[1], result[2], result[3]\n\t}\n\tif err := teststat.TestHistogram(histogram, quantiles, 0.01); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestHistogramLabels(t *testing.T) {\n\tin := New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger())\n\th := in.NewHistogram(\"foo\")\n\th.Observe(123)\n\th.With(\"abc\", \"xyz\").Observe(456)\n\tw := &bufWriter{}\n\tif err := in.WriteTo(w); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif want, have := 2, len(strings.Split(strings.TrimSpace(w.buf.String()), \"\\n\")); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\nfunc TestIssue404(t *testing.T) {\n\tin := New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger())\n\n\tcounterOne := in.NewCounter(\"influx_counter_one\").With(\"a\", \"b\")\n\tcounterOne.Add(123)\n\n\tcounterTwo := in.NewCounter(\"influx_counter_two\").With(\"c\", \"d\")\n\tcounterTwo.Add(456)\n\n\tw := &bufWriter{}\n\tin.WriteTo(w)\n\n\tlines := strings.Split(strings.TrimSpace(w.buf.String()), \"\\n\")\n\tif want, have := 2, len(lines); want != have {\n\t\tt.Fatalf(\"want %d, have %d\", want, have)\n\t}\n\tfor _, line := range lines {\n\t\tif strings.HasPrefix(line, \"influx_counter_one\") {\n\t\t\tif !strings.HasPrefix(line, \"influx_counter_one,a=b count=123 \") {\n\t\t\t\tt.Errorf(\"invalid influx_counter_one: %s\", line)\n\t\t\t}\n\t\t} else if strings.HasPrefix(line, \"influx_counter_two\") {\n\t\t\tif !strings.HasPrefix(line, \"influx_counter_two,c=d count=456 \") {\n\t\t\t\tt.Errorf(\"invalid influx_counter_two: %s\", line)\n\t\t\t}\n\t\t} else {\n\t\t\tt.Errorf(\"unexpected line: %s\", line)\n\t\t}\n\t}\n}\n\ntype bufWriter struct {\n\tbuf bytes.Buffer\n}\n\nfunc (w *bufWriter) Write(bp influxdb.BatchPoints) error {\n\tfor _, p := range bp.Points() {\n\t\tfmt.Fprintf(&w.buf, p.String()+\"\\n\")\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "metrics/influxstatsd/influxstatsd.go",
    "content": "// Package influxstatsd provides support for InfluxData's StatsD Telegraf plugin. It's very\n// similar to StatsD, but supports arbitrary tags per-metric, which map to Go\n// kit's label values. So, while label values are no-ops in StatsD, they are\n// supported here. For more details, see the article at\n// https://www.influxdata.com/blog/getting-started-with-sending-statsd-metrics-to-telegraf-influxdb/\n//\n// This package batches observations and emits them on some schedule to the\n// remote server. This is useful even if you connect to your service\n// over UDP. Emitting one network packet per observation can quickly overwhelm\n// even the fastest internal network.\npackage influxstatsd\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/generic\"\n\t\"github.com/go-kit/kit/metrics/internal/lv\"\n\t\"github.com/go-kit/kit/metrics/internal/ratemap\"\n\t\"github.com/go-kit/kit/util/conn\"\n\t\"github.com/go-kit/log\"\n)\n\n// Influxstatsd receives metrics observations and forwards them to a server.\n// Create a Influxstatsd object, use it to create metrics, and pass those\n// metrics as dependencies to the components that will use them.\n//\n// All metrics are buffered until WriteTo is called. Counters and gauges are\n// aggregated into a single observation per timeseries per write. Timings and\n// histograms are buffered but not aggregated.\n//\n// To regularly report metrics to an io.Writer, use the WriteLoop helper method.\n// To send to a InfluxStatsD server, use the SendLoop helper method.\ntype Influxstatsd struct {\n\tmtx        sync.RWMutex\n\tprefix     string\n\trates      *ratemap.RateMap\n\tcounters   *lv.Space\n\tgauges     map[string]*gaugeNode\n\ttimings    *lv.Space\n\thistograms *lv.Space\n\tlogger     log.Logger\n\tlvs        lv.LabelValues\n}\n\n// New returns a Influxstatsd object that may be used to create metrics. Prefix is\n// applied to all created metrics. Callers must ensure that regular calls to\n// WriteTo are performed, either manually or with one of the helper methods.\nfunc New(prefix string, logger log.Logger, lvs ...string) *Influxstatsd {\n\tif len(lvs)%2 != 0 {\n\t\tpanic(\"odd number of LabelValues; programmer error!\")\n\t}\n\treturn &Influxstatsd{\n\t\tprefix:     prefix,\n\t\trates:      ratemap.New(),\n\t\tcounters:   lv.NewSpace(),\n\t\tgauges:     map[string]*gaugeNode{}, // https://github.com/go-kit/kit/pull/588\n\t\ttimings:    lv.NewSpace(),\n\t\thistograms: lv.NewSpace(),\n\t\tlogger:     logger,\n\t\tlvs:        lvs,\n\t}\n}\n\n// NewCounter returns a counter, sending observations to this Influxstatsd object.\nfunc (d *Influxstatsd) NewCounter(name string, sampleRate float64) *Counter {\n\td.rates.Set(name, sampleRate)\n\treturn &Counter{\n\t\tname: name,\n\t\tobs:  d.counters.Observe,\n\t}\n}\n\n// NewGauge returns a gauge, sending observations to this Influxstatsd object.\nfunc (d *Influxstatsd) NewGauge(name string) *Gauge {\n\td.mtx.Lock()\n\tn, ok := d.gauges[name]\n\tif !ok {\n\t\tn = &gaugeNode{gauge: &Gauge{g: generic.NewGauge(name), influx: d}}\n\t\td.gauges[name] = n\n\t}\n\td.mtx.Unlock()\n\treturn n.gauge\n}\n\n// NewTiming returns a histogram whose observations are interpreted as\n// millisecond durations, and are forwarded to this Influxstatsd object.\nfunc (d *Influxstatsd) NewTiming(name string, sampleRate float64) *Timing {\n\td.rates.Set(name, sampleRate)\n\treturn &Timing{\n\t\tname: name,\n\t\tobs:  d.timings.Observe,\n\t}\n}\n\n// NewHistogram returns a histogram whose observations are of an unspecified\n// unit, and are forwarded to this Influxstatsd object.\nfunc (d *Influxstatsd) NewHistogram(name string, sampleRate float64) *Histogram {\n\td.rates.Set(name, sampleRate)\n\treturn &Histogram{\n\t\tname: name,\n\t\tobs:  d.histograms.Observe,\n\t}\n}\n\n// WriteLoop is a helper method that invokes WriteTo to the passed writer every\n// time the passed channel fires. This method blocks until ctx is canceled,\n// so clients probably want to run it in its own goroutine. For typical\n// usage, create a time.Ticker and pass its C channel to this method.\nfunc (d *Influxstatsd) WriteLoop(ctx context.Context, c <-chan time.Time, w io.Writer) {\n\tfor {\n\t\tselect {\n\t\tcase <-c:\n\t\t\tif _, err := d.WriteTo(w); err != nil {\n\t\t\t\td.logger.Log(\"during\", \"WriteTo\", \"err\", err)\n\t\t\t}\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// SendLoop is a helper method that wraps WriteLoop, passing a managed\n// connection to the network and address. Like WriteLoop, this method blocks\n// until ctx is canceled, so clients probably want to start it in its own\n// goroutine. For typical usage, create a time.Ticker and pass its C channel to\n// this method.\nfunc (d *Influxstatsd) SendLoop(ctx context.Context, c <-chan time.Time, network, address string) {\n\td.WriteLoop(ctx, c, conn.NewDefaultManager(network, address, d.logger))\n}\n\n// WriteTo flushes the buffered content of the metrics to the writer, in\n// InfluxStatsD format. WriteTo abides best-effort semantics, so observations are\n// lost if there is a problem with the write. Clients should be sure to call\n// WriteTo regularly, ideally through the WriteLoop or SendLoop helper methods.\nfunc (d *Influxstatsd) WriteTo(w io.Writer) (count int64, err error) {\n\tvar n int\n\n\td.counters.Reset().Walk(func(name string, lvs lv.LabelValues, values []float64) bool {\n\t\tn, err = fmt.Fprintf(w, \"%s%s%s:%f|c%s\\n\", d.prefix, name, d.tagValues(lvs), sum(values), sampling(d.rates.Get(name)))\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tcount += int64(n)\n\t\treturn true\n\t})\n\tif err != nil {\n\t\treturn count, err\n\t}\n\n\td.mtx.RLock()\n\tfor _, root := range d.gauges {\n\t\troot.walk(func(name string, lvs lv.LabelValues, value float64) bool {\n\t\t\tn, err = fmt.Fprintf(w, \"%s%s%s:%f|g\\n\", d.prefix, name, d.tagValues(lvs), value)\n\t\t\tif err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tcount += int64(n)\n\t\t\treturn true\n\t\t})\n\t}\n\td.mtx.RUnlock()\n\n\td.timings.Reset().Walk(func(name string, lvs lv.LabelValues, values []float64) bool {\n\t\tsampleRate := d.rates.Get(name)\n\t\tfor _, value := range values {\n\t\t\tn, err = fmt.Fprintf(w, \"%s%s%s:%f|ms%s\\n\", d.prefix, name, d.tagValues(lvs), value, sampling(sampleRate))\n\t\t\tif err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tcount += int64(n)\n\t\t}\n\t\treturn true\n\t})\n\tif err != nil {\n\t\treturn count, err\n\t}\n\n\td.histograms.Reset().Walk(func(name string, lvs lv.LabelValues, values []float64) bool {\n\t\tsampleRate := d.rates.Get(name)\n\t\tfor _, value := range values {\n\t\t\tn, err = fmt.Fprintf(w, \"%s%s%s:%f|h%s\\n\", d.prefix, name, d.tagValues(lvs), value, sampling(sampleRate))\n\t\t\tif err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tcount += int64(n)\n\t\t}\n\t\treturn true\n\t})\n\tif err != nil {\n\t\treturn count, err\n\t}\n\n\treturn count, err\n}\n\nfunc sum(a []float64) float64 {\n\tvar v float64\n\tfor _, f := range a {\n\t\tv += f\n\t}\n\treturn v\n}\n\nfunc sampling(r float64) string {\n\tvar sv string\n\tif r < 1.0 {\n\t\tsv = fmt.Sprintf(\"|@%f\", r)\n\t}\n\treturn sv\n}\n\nfunc (d *Influxstatsd) tagValues(labelValues []string) string {\n\tif len(labelValues) == 0 && len(d.lvs) == 0 {\n\t\treturn \"\"\n\t}\n\tif len(labelValues)%2 != 0 {\n\t\tpanic(\"tagValues received a labelValues with an odd number of strings\")\n\t}\n\tpairs := make([]string, 0, (len(d.lvs)+len(labelValues))/2)\n\tfor i := 0; i < len(d.lvs); i += 2 {\n\t\tpairs = append(pairs, d.lvs[i]+\"=\"+d.lvs[i+1])\n\t}\n\tfor i := 0; i < len(labelValues); i += 2 {\n\t\tpairs = append(pairs, labelValues[i]+\"=\"+labelValues[i+1])\n\t}\n\treturn \",\" + strings.Join(pairs, \",\")\n}\n\ntype observeFunc func(name string, lvs lv.LabelValues, value float64)\n\n// Counter is a InfluxStatsD counter. Observations are forwarded to a Influxstatsd\n// object, and aggregated (summed) per timeseries.\ntype Counter struct {\n\tname string\n\tlvs  lv.LabelValues\n\tobs  observeFunc\n}\n\n// With implements metrics.Counter.\nfunc (c *Counter) With(labelValues ...string) metrics.Counter {\n\treturn &Counter{\n\t\tname: c.name,\n\t\tlvs:  c.lvs.With(labelValues...),\n\t\tobs:  c.obs,\n\t}\n}\n\n// Add implements metrics.Counter.\nfunc (c *Counter) Add(delta float64) {\n\tc.obs(c.name, c.lvs, delta)\n}\n\n// Gauge is a InfluxStatsD gauge. Observations are forwarded to a Influxstatsd\n// object, and aggregated (the last observation selected) per timeseries.\ntype Gauge struct {\n\tg      *generic.Gauge\n\tinflux *Influxstatsd\n\tset    int32\n}\n\n// With implements metrics.Gauge.\nfunc (g *Gauge) With(labelValues ...string) metrics.Gauge {\n\tg.influx.mtx.RLock()\n\tnode := g.influx.gauges[g.g.Name]\n\tg.influx.mtx.RUnlock()\n\n\tga := &Gauge{g: g.g.With(labelValues...).(*generic.Gauge), influx: g.influx}\n\treturn node.addGauge(ga, ga.g.LabelValues())\n}\n\n// Set implements metrics.Gauge.\nfunc (g *Gauge) Set(value float64) {\n\tg.g.Set(value)\n\tg.touch()\n}\n\n// Add implements metrics.Gauge.\nfunc (g *Gauge) Add(delta float64) {\n\tg.g.Add(delta)\n\tg.touch()\n}\n\n// Timing is a InfluxStatsD timing, or metrics.Histogram. Observations are\n// forwarded to a Influxstatsd object, and collected (but not aggregated) per\n// timeseries.\ntype Timing struct {\n\tname string\n\tlvs  lv.LabelValues\n\tobs  observeFunc\n}\n\n// With implements metrics.Timing.\nfunc (t *Timing) With(labelValues ...string) metrics.Histogram {\n\treturn &Timing{\n\t\tname: t.name,\n\t\tlvs:  t.lvs.With(labelValues...),\n\t\tobs:  t.obs,\n\t}\n}\n\n// Observe implements metrics.Histogram. Value is interpreted as milliseconds.\nfunc (t *Timing) Observe(value float64) {\n\tt.obs(t.name, t.lvs, value)\n}\n\n// Histogram is a InfluxStatsD histrogram. Observations are forwarded to a\n// Influxstatsd object, and collected (but not aggregated) per timeseries.\ntype Histogram struct {\n\tname string\n\tlvs  lv.LabelValues\n\tobs  observeFunc\n}\n\n// With implements metrics.Histogram.\nfunc (h *Histogram) With(labelValues ...string) metrics.Histogram {\n\treturn &Histogram{\n\t\tname: h.name,\n\t\tlvs:  h.lvs.With(labelValues...),\n\t\tobs:  h.obs,\n\t}\n}\n\n// Observe implements metrics.Histogram.\nfunc (h *Histogram) Observe(value float64) {\n\th.obs(h.name, h.lvs, value)\n}\n\ntype pair struct{ label, value string }\n\ntype gaugeNode struct {\n\tmtx      sync.RWMutex\n\tgauge    *Gauge\n\tchildren map[pair]*gaugeNode\n}\n\nfunc (n *gaugeNode) addGauge(g *Gauge, lvs lv.LabelValues) *Gauge {\n\tn.mtx.Lock()\n\tdefer n.mtx.Unlock()\n\tif len(lvs) == 0 {\n\t\tif n.gauge == nil {\n\t\t\tn.gauge = g\n\t\t}\n\t\treturn n.gauge\n\t}\n\tif len(lvs) < 2 {\n\t\tpanic(\"too few LabelValues; programmer error!\")\n\t}\n\thead, tail := pair{lvs[0], lvs[1]}, lvs[2:]\n\tif n.children == nil {\n\t\tn.children = map[pair]*gaugeNode{}\n\t}\n\tchild, ok := n.children[head]\n\tif !ok {\n\t\tchild = &gaugeNode{}\n\t\tn.children[head] = child\n\t}\n\treturn child.addGauge(g, tail)\n}\n\nfunc (n *gaugeNode) walk(fn func(string, lv.LabelValues, float64) bool) bool {\n\tn.mtx.RLock()\n\tdefer n.mtx.RUnlock()\n\tif n.gauge != nil {\n\t\tvalue, ok := n.gauge.read()\n\t\tif ok && !fn(n.gauge.g.Name, n.gauge.g.LabelValues(), value) {\n\t\t\treturn false\n\t\t}\n\t}\n\tfor _, child := range n.children {\n\t\tif !child.walk(fn) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (g *Gauge) touch() {\n\tatomic.StoreInt32(&(g.set), 1)\n}\n\nfunc (g *Gauge) read() (float64, bool) {\n\tset := atomic.SwapInt32(&(g.set), 0)\n\treturn g.g.Value(), set != 0\n}\n"
  },
  {
    "path": "metrics/influxstatsd/influxstatsd_test.go",
    "content": "package influxstatsd\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-kit/kit/metrics/teststat\"\n\t\"github.com/go-kit/log\"\n)\n\nfunc TestCounter(t *testing.T) {\n\tprefix, name := \"abc.\", \"def\"\n\tlabel, value := \"label\", \"value\"\n\tregex := `^` + prefix + name + \",\" + label + `=` + value + `:([0-9\\.]+)\\|c$`\n\td := New(prefix, log.NewNopLogger())\n\tcounter := d.NewCounter(name, 1.0).With(label, value)\n\tvaluef := teststat.SumLines(d, regex)\n\tif err := teststat.TestCounter(counter, valuef); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestCounterSampled(t *testing.T) {\n\t// This will involve multiplying the observed sum by the inverse of the\n\t// sample rate and checking against the expected value within some\n\t// tolerance.\n\tt.Skip(\"TODO\")\n}\n\nfunc TestGauge(t *testing.T) {\n\tprefix, name := \"ghi.\", \"jkl\"\n\tlabel, value := \"xyz\", \"abc\"\n\tregex := `^` + prefix + name + `,hostname=foohost,` + label + `=` + value + `:([0-9\\.]+)\\|g$`\n\td := New(prefix, log.NewNopLogger(), \"hostname\", \"foohost\")\n\tgauge := d.NewGauge(name).With(label, value)\n\tvaluef := teststat.LastLine(d, regex)\n\tif err := teststat.TestGauge(gauge, valuef); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\n// InfluxStatsD histograms just emit all observations. So, we collect them into\n// a generic histogram, and run the statistics test on that.\n\nfunc TestHistogram(t *testing.T) {\n\tprefix, name := \"influxstatsd.\", \"histogram_test\"\n\tlabel, value := \"abc\", \"def\"\n\tregex := `^` + prefix + name + \",\" + label + `=` + value + `:([0-9\\.]+)\\|h$`\n\td := New(prefix, log.NewNopLogger())\n\thistogram := d.NewHistogram(name, 1.0).With(label, value)\n\tquantiles := teststat.Quantiles(d, regex, 50) // no |@0.X\n\tif err := teststat.TestHistogram(histogram, quantiles, 0.01); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestHistogramSampled(t *testing.T) {\n\tprefix, name := \"influxstatsd.\", \"sampled_histogram_test\"\n\tlabel, value := \"foo\", \"bar\"\n\tregex := `^` + prefix + name + \",\" + label + `=` + value + `:([0-9\\.]+)\\|h\\|@0\\.01[0]*$`\n\td := New(prefix, log.NewNopLogger())\n\thistogram := d.NewHistogram(name, 0.01).With(label, value)\n\tquantiles := teststat.Quantiles(d, regex, 50)\n\tif err := teststat.TestHistogram(histogram, quantiles, 0.02); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestTiming(t *testing.T) {\n\tprefix, name := \"influxstatsd.\", \"timing_test\"\n\tlabel, value := \"wiggle\", \"bottom\"\n\tregex := `^` + prefix + name + \",\" + label + `=` + value + `:([0-9\\.]+)\\|ms$`\n\td := New(prefix, log.NewNopLogger())\n\thistogram := d.NewTiming(name, 1.0).With(label, value)\n\tquantiles := teststat.Quantiles(d, regex, 50) // no |@0.X\n\tif err := teststat.TestHistogram(histogram, quantiles, 0.01); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestTimingSampled(t *testing.T) {\n\tprefix, name := \"influxstatsd.\", \"sampled_timing_test\"\n\tlabel, value := \"internal\", \"external\"\n\tregex := `^` + prefix + name + \",\" + label + `=` + value + `:([0-9\\.]+)\\|ms\\|@0.03[0]*$`\n\td := New(prefix, log.NewNopLogger())\n\thistogram := d.NewTiming(name, 0.03).With(label, value)\n\tquantiles := teststat.Quantiles(d, regex, 50)\n\tif err := teststat.TestHistogram(histogram, quantiles, 0.02); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "metrics/internal/convert/convert.go",
    "content": "// Package convert provides a way to use Counters, Histograms, or Gauges\n// as one of the other types\npackage convert\n\nimport \"github.com/go-kit/kit/metrics\"\n\ntype counterHistogram struct {\n\tc metrics.Counter\n}\n\n// NewCounterAsHistogram returns a Histogram that actually writes the\n// value on an underlying Counter\nfunc NewCounterAsHistogram(c metrics.Counter) metrics.Histogram {\n\treturn counterHistogram{c}\n}\n\n// With implements Histogram.\nfunc (ch counterHistogram) With(labelValues ...string) metrics.Histogram {\n\treturn counterHistogram{ch.c.With(labelValues...)}\n}\n\n// Observe implements histogram.\nfunc (ch counterHistogram) Observe(value float64) {\n\tch.c.Add(value)\n}\n\ntype histogramCounter struct {\n\th metrics.Histogram\n}\n\n// NewHistogramAsCounter returns a Counter that actually writes the\n// value on an underlying Histogram\nfunc NewHistogramAsCounter(h metrics.Histogram) metrics.Counter {\n\treturn histogramCounter{h}\n}\n\n// With implements Counter.\nfunc (hc histogramCounter) With(labelValues ...string) metrics.Counter {\n\treturn histogramCounter{hc.h.With(labelValues...)}\n}\n\n// Add implements Counter.\nfunc (hc histogramCounter) Add(delta float64) {\n\thc.h.Observe(delta)\n}\n\ntype counterGauge struct {\n\tc metrics.Counter\n}\n\n// NewCounterAsGauge returns a Gauge that actually writes the\n// value on an underlying Counter\nfunc NewCounterAsGauge(c metrics.Counter) metrics.Gauge {\n\treturn counterGauge{c}\n}\n\n// With implements Gauge.\nfunc (cg counterGauge) With(labelValues ...string) metrics.Gauge {\n\treturn counterGauge{cg.c.With(labelValues...)}\n}\n\n// Set implements Gauge.\nfunc (cg counterGauge) Set(value float64) {\n\tcg.c.Add(value)\n}\n\n// Add implements metrics.Gauge.\nfunc (cg counterGauge) Add(delta float64) {\n\tcg.c.Add(delta)\n}\n\ntype gaugeCounter struct {\n\tg metrics.Gauge\n}\n\n// NewGaugeAsCounter returns a Counter that actually writes the\n// value on an underlying Gauge\nfunc NewGaugeAsCounter(g metrics.Gauge) metrics.Counter {\n\treturn gaugeCounter{g}\n}\n\n// With implements Counter.\nfunc (gc gaugeCounter) With(labelValues ...string) metrics.Counter {\n\treturn gaugeCounter{gc.g.With(labelValues...)}\n}\n\n// Add implements Counter.\nfunc (gc gaugeCounter) Add(delta float64) {\n\tgc.g.Set(delta)\n}\n\ntype histogramGauge struct {\n\th metrics.Histogram\n}\n\n// NewHistogramAsGauge returns a Gauge that actually writes the\n// value on an underlying Histogram\nfunc NewHistogramAsGauge(h metrics.Histogram) metrics.Gauge {\n\treturn histogramGauge{h}\n}\n\n// With implements Gauge.\nfunc (hg histogramGauge) With(labelValues ...string) metrics.Gauge {\n\treturn histogramGauge{hg.h.With(labelValues...)}\n}\n\n// Set implements Gauge.\nfunc (hg histogramGauge) Set(value float64) {\n\thg.h.Observe(value)\n}\n\n// Add implements metrics.Gauge.\nfunc (hg histogramGauge) Add(delta float64) {\n\thg.h.Observe(delta)\n}\n\ntype gaugeHistogram struct {\n\tg metrics.Gauge\n}\n\n// NewGaugeAsHistogram returns a Histogram that actually writes the\n// value on an underlying Gauge\nfunc NewGaugeAsHistogram(g metrics.Gauge) metrics.Histogram {\n\treturn gaugeHistogram{g}\n}\n\n// With implements Histogram.\nfunc (gh gaugeHistogram) With(labelValues ...string) metrics.Histogram {\n\treturn gaugeHistogram{gh.g.With(labelValues...)}\n}\n\n// Observe implements histogram.\nfunc (gh gaugeHistogram) Observe(value float64) {\n\tgh.g.Set(value)\n}\n"
  },
  {
    "path": "metrics/internal/convert/convert_test.go",
    "content": "package convert\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-kit/kit/metrics/generic\"\n\t\"github.com/go-kit/kit/metrics/teststat\"\n)\n\nfunc TestCounterHistogramConversion(t *testing.T) {\n\tname := \"my_counter\"\n\tc := generic.NewCounter(name)\n\th := NewCounterAsHistogram(c)\n\ttop := NewHistogramAsCounter(h).With(\"label\", \"counter\").(histogramCounter)\n\tmid := top.h.(counterHistogram)\n\tlow := mid.c.(*generic.Counter)\n\tif want, have := name, low.Name; want != have {\n\t\tt.Errorf(\"Name: want %q, have %q\", want, have)\n\t}\n\tif err := teststat.TestCounter(top, low.Value); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestCounterGaugeConversion(t *testing.T) {\n\tname := \"my_counter\"\n\tc := generic.NewCounter(name)\n\tg := NewCounterAsGauge(c)\n\ttop := NewGaugeAsCounter(g).With(\"label\", \"counter\").(gaugeCounter)\n\tmid := top.g.(counterGauge)\n\tlow := mid.c.(*generic.Counter)\n\tif want, have := name, low.Name; want != have {\n\t\tt.Errorf(\"Name: want %q, have %q\", want, have)\n\t}\n\tif err := teststat.TestCounter(top, low.Value); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestHistogramGaugeConversion(t *testing.T) {\n\tname := \"my_histogram\"\n\th := generic.NewHistogram(name, 50)\n\tg := NewHistogramAsGauge(h)\n\ttop := NewGaugeAsHistogram(g).With(\"label\", \"histogram\").(gaugeHistogram)\n\tmid := top.g.(histogramGauge)\n\tlow := mid.h.(*generic.Histogram)\n\tif want, have := name, low.Name; want != have {\n\t\tt.Errorf(\"Name: want %q, have %q\", want, have)\n\t}\n\tquantiles := func() (float64, float64, float64, float64) {\n\t\treturn low.Quantile(0.50), low.Quantile(0.90), low.Quantile(0.95), low.Quantile(0.99)\n\t}\n\tif err := teststat.TestHistogram(top, quantiles, 0.01); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "metrics/internal/lv/labelvalues.go",
    "content": "package lv\n\n// LabelValues is a type alias that provides validation on its With method.\n// Metrics may include it as a member to help them satisfy With semantics and\n// save some code duplication.\ntype LabelValues []string\n\n// With validates the input, and returns a new aggregate labelValues.\nfunc (lvs LabelValues) With(labelValues ...string) LabelValues {\n\tif len(labelValues)%2 != 0 {\n\t\tlabelValues = append(labelValues, \"unknown\")\n\t}\n\treturn append(lvs, labelValues...)\n}\n"
  },
  {
    "path": "metrics/internal/lv/labelvalues_test.go",
    "content": "package lv\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestWith(t *testing.T) {\n\tvar a LabelValues\n\tb := a.With(\"a\", \"1\")\n\tc := a.With(\"b\", \"2\", \"c\", \"3\")\n\n\tif want, have := \"\", strings.Join(a, \"\"); want != have {\n\t\tt.Errorf(\"With appears to mutate the original LabelValues: want %q, have %q\", want, have)\n\t}\n\tif want, have := \"a1\", strings.Join(b, \"\"); want != have {\n\t\tt.Errorf(\"With does not appear to return the right thing: want %q, have %q\", want, have)\n\t}\n\tif want, have := \"b2c3\", strings.Join(c, \"\"); want != have {\n\t\tt.Errorf(\"With does not appear to return the right thing: want %q, have %q\", want, have)\n\t}\n}\n"
  },
  {
    "path": "metrics/internal/lv/space.go",
    "content": "package lv\n\nimport \"sync\"\n\n// NewSpace returns an N-dimensional vector space.\nfunc NewSpace() *Space {\n\treturn &Space{}\n}\n\n// Space represents an N-dimensional vector space. Each name and unique label\n// value pair establishes a new dimension and point within that dimension. Order\n// matters, i.e. [a=1 b=2] identifies a different timeseries than [b=2 a=1].\ntype Space struct {\n\tmtx   sync.RWMutex\n\tnodes map[string]*node\n}\n\n// Observe locates the time series identified by the name and label values in\n// the vector space, and appends the value to the list of observations.\nfunc (s *Space) Observe(name string, lvs LabelValues, value float64) {\n\ts.nodeFor(name).observe(lvs, value)\n}\n\n// Add locates the time series identified by the name and label values in\n// the vector space, and appends the delta to the last value in the list of\n// observations.\nfunc (s *Space) Add(name string, lvs LabelValues, delta float64) {\n\ts.nodeFor(name).add(lvs, delta)\n}\n\n// Walk traverses the vector space and invokes fn for each non-empty time series\n// which is encountered. Return false to abort the traversal.\nfunc (s *Space) Walk(fn func(name string, lvs LabelValues, observations []float64) bool) {\n\ts.mtx.RLock()\n\tdefer s.mtx.RUnlock()\n\tfor name, node := range s.nodes {\n\t\tf := func(lvs LabelValues, observations []float64) bool { return fn(name, lvs, observations) }\n\t\tif !node.walk(LabelValues{}, f) {\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// Reset empties the current space and returns a new Space with the old\n// contents. Reset a Space to get an immutable copy suitable for walking.\nfunc (s *Space) Reset() *Space {\n\ts.mtx.Lock()\n\tdefer s.mtx.Unlock()\n\tn := NewSpace()\n\tn.nodes, s.nodes = s.nodes, n.nodes\n\treturn n\n}\n\nfunc (s *Space) nodeFor(name string) *node {\n\ts.mtx.Lock()\n\tdefer s.mtx.Unlock()\n\tif s.nodes == nil {\n\t\ts.nodes = map[string]*node{}\n\t}\n\tn, ok := s.nodes[name]\n\tif !ok {\n\t\tn = &node{}\n\t\ts.nodes[name] = n\n\t}\n\treturn n\n}\n\n// node exists at a specific point in the N-dimensional vector space of all\n// possible label values. The node collects observations and has child nodes\n// with greater specificity.\ntype node struct {\n\tmtx          sync.RWMutex\n\tobservations []float64\n\tchildren     map[pair]*node\n}\n\ntype pair struct{ label, value string }\n\nfunc (n *node) observe(lvs LabelValues, value float64) {\n\tn.mtx.Lock()\n\tdefer n.mtx.Unlock()\n\tif len(lvs) <= 0 {\n\t\tn.observations = append(n.observations, value)\n\t\treturn\n\t}\n\tif len(lvs) < 2 {\n\t\tpanic(\"too few LabelValues; programmer error!\")\n\t}\n\thead, tail := pair{lvs[0], lvs[1]}, lvs[2:]\n\tif n.children == nil {\n\t\tn.children = map[pair]*node{}\n\t}\n\tchild, ok := n.children[head]\n\tif !ok {\n\t\tchild = &node{}\n\t\tn.children[head] = child\n\t}\n\tchild.observe(tail, value)\n}\n\nfunc (n *node) add(lvs LabelValues, delta float64) {\n\tn.mtx.Lock()\n\tdefer n.mtx.Unlock()\n\tif len(lvs) <= 0 {\n\t\tvar value float64\n\t\tif len(n.observations) > 0 {\n\t\t\tvalue = last(n.observations) + delta\n\t\t} else {\n\t\t\tvalue = delta\n\t\t}\n\t\tn.observations = append(n.observations, value)\n\t\treturn\n\t}\n\tif len(lvs) < 2 {\n\t\tpanic(\"too few LabelValues; programmer error!\")\n\t}\n\thead, tail := pair{lvs[0], lvs[1]}, lvs[2:]\n\tif n.children == nil {\n\t\tn.children = map[pair]*node{}\n\t}\n\tchild, ok := n.children[head]\n\tif !ok {\n\t\tchild = &node{}\n\t\tn.children[head] = child\n\t}\n\tchild.add(tail, delta)\n}\n\nfunc (n *node) walk(lvs LabelValues, fn func(LabelValues, []float64) bool) bool {\n\tn.mtx.RLock()\n\tdefer n.mtx.RUnlock()\n\tif len(n.observations) > 0 && !fn(lvs, n.observations) {\n\t\treturn false\n\t}\n\tfor p, child := range n.children {\n\t\tif !child.walk(append(lvs, p.label, p.value), fn) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc last(a []float64) float64 {\n\treturn a[len(a)-1]\n}\n"
  },
  {
    "path": "metrics/internal/lv/space_test.go",
    "content": "package lv\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestSpaceWalkAbort(t *testing.T) {\n\ts := NewSpace()\n\ts.Observe(\"a\", LabelValues{\"a\", \"b\"}, 1)\n\ts.Observe(\"a\", LabelValues{\"c\", \"d\"}, 2)\n\ts.Observe(\"a\", LabelValues{\"e\", \"f\"}, 4)\n\ts.Observe(\"a\", LabelValues{\"g\", \"h\"}, 8)\n\ts.Observe(\"b\", LabelValues{\"a\", \"b\"}, 16)\n\ts.Observe(\"b\", LabelValues{\"c\", \"d\"}, 32)\n\ts.Observe(\"b\", LabelValues{\"e\", \"f\"}, 64)\n\ts.Observe(\"b\", LabelValues{\"g\", \"h\"}, 128)\n\n\tvar count int\n\ts.Walk(func(name string, lvs LabelValues, obs []float64) bool {\n\t\tcount++\n\t\treturn false\n\t})\n\tif want, have := 1, count; want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\nfunc TestSpaceWalkSums(t *testing.T) {\n\ts := NewSpace()\n\ts.Observe(\"metric_one\", LabelValues{}, 1)\n\ts.Observe(\"metric_one\", LabelValues{}, 2)\n\ts.Observe(\"metric_one\", LabelValues{\"a\", \"1\", \"b\", \"2\"}, 4)\n\ts.Observe(\"metric_one\", LabelValues{\"a\", \"1\", \"b\", \"2\"}, 8)\n\ts.Observe(\"metric_one\", LabelValues{}, 16)\n\ts.Observe(\"metric_one\", LabelValues{\"a\", \"1\", \"b\", \"3\"}, 32)\n\ts.Observe(\"metric_two\", LabelValues{}, 64)\n\ts.Observe(\"metric_two\", LabelValues{}, 128)\n\ts.Observe(\"metric_two\", LabelValues{\"a\", \"1\", \"b\", \"2\"}, 256)\n\n\thave := map[string]float64{}\n\ts.Walk(func(name string, lvs LabelValues, obs []float64) bool {\n\t\thave[name+\" [\"+strings.Join(lvs, \"\")+\"]\"] += sum(obs)\n\t\treturn true\n\t})\n\n\twant := map[string]float64{\n\t\t\"metric_one []\":     1 + 2 + 16,\n\t\t\"metric_one [a1b2]\": 4 + 8,\n\t\t\"metric_one [a1b3]\": 32,\n\t\t\"metric_two []\":     64 + 128,\n\t\t\"metric_two [a1b2]\": 256,\n\t}\n\tfor keystr, wantsum := range want {\n\t\tif havesum := have[keystr]; wantsum != havesum {\n\t\t\tt.Errorf(\"%q: want %.1f, have %.1f\", keystr, wantsum, havesum)\n\t\t}\n\t\tdelete(want, keystr)\n\t\tdelete(have, keystr)\n\t}\n\tfor keystr, havesum := range have {\n\t\tt.Errorf(\"%q: unexpected observations recorded: %.1f\", keystr, havesum)\n\t}\n}\n\nfunc TestSpaceWalkSkipsEmptyDimensions(t *testing.T) {\n\ts := NewSpace()\n\ts.Observe(\"foo\", LabelValues{\"bar\", \"1\", \"baz\", \"2\"}, 123)\n\n\tvar count int\n\ts.Walk(func(name string, lvs LabelValues, obs []float64) bool {\n\t\tcount++\n\t\treturn true\n\t})\n\tif want, have := 1, count; want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\nfunc sum(a []float64) (v float64) {\n\tfor _, f := range a {\n\t\tv += f\n\t}\n\treturn\n}\n"
  },
  {
    "path": "metrics/internal/ratemap/ratemap.go",
    "content": "// Package ratemap implements a goroutine-safe map of string to float64. It can\n// be embedded in implementations whose metrics support fixed sample rates, so\n// that an additional parameter doesn't have to be tracked through the e.g.\n// lv.Space object.\npackage ratemap\n\nimport \"sync\"\n\n// RateMap is a simple goroutine-safe map of string to float64.\ntype RateMap struct {\n\tmtx sync.RWMutex\n\tm   map[string]float64\n}\n\n// New returns a new RateMap.\nfunc New() *RateMap {\n\treturn &RateMap{\n\t\tm: map[string]float64{},\n\t}\n}\n\n// Set writes the given name/rate pair to the map.\n// Set is safe for concurrent access by multiple goroutines.\nfunc (m *RateMap) Set(name string, rate float64) {\n\tm.mtx.Lock()\n\tdefer m.mtx.Unlock()\n\tm.m[name] = rate\n}\n\n// Get retrieves the rate for the given name, or 1.0 if none is set.\n// Get is safe for concurrent access by multiple goroutines.\nfunc (m *RateMap) Get(name string) float64 {\n\tm.mtx.RLock()\n\tdefer m.mtx.RUnlock()\n\tf, ok := m.m[name]\n\tif !ok {\n\t\tf = 1.0\n\t}\n\treturn f\n}\n"
  },
  {
    "path": "metrics/metrics.go",
    "content": "package metrics\n\n// Counter describes a metric that accumulates values monotonically.\n// An example of a counter is the number of received HTTP requests.\ntype Counter interface {\n\tWith(labelValues ...string) Counter\n\tAdd(delta float64)\n}\n\n// Gauge describes a metric that takes specific values over time.\n// An example of a gauge is the current depth of a job queue.\ntype Gauge interface {\n\tWith(labelValues ...string) Gauge\n\tSet(value float64)\n\tAdd(delta float64)\n}\n\n// Histogram describes a metric that takes repeated observations of the same\n// kind of thing, and produces a statistical summary of those observations,\n// typically expressed as quantiles or buckets. An example of a histogram is\n// HTTP request latencies.\ntype Histogram interface {\n\tWith(labelValues ...string) Histogram\n\tObserve(value float64)\n}\n"
  },
  {
    "path": "metrics/multi/multi.go",
    "content": "// Package multi provides adapters that send observations to multiple metrics\n// simultaneously. This is useful if your service needs to emit to multiple\n// instrumentation systems at the same time, for example if your organization is\n// transitioning from one system to another.\npackage multi\n\nimport \"github.com/go-kit/kit/metrics\"\n\n// Counter collects multiple individual counters and treats them as a unit.\ntype Counter []metrics.Counter\n\n// NewCounter returns a multi-counter, wrapping the passed counters.\nfunc NewCounter(c ...metrics.Counter) Counter {\n\treturn Counter(c)\n}\n\n// Add implements counter.\nfunc (c Counter) Add(delta float64) {\n\tfor _, counter := range c {\n\t\tcounter.Add(delta)\n\t}\n}\n\n// With implements counter.\nfunc (c Counter) With(labelValues ...string) metrics.Counter {\n\tnext := make(Counter, len(c))\n\tfor i := range c {\n\t\tnext[i] = c[i].With(labelValues...)\n\t}\n\treturn next\n}\n\n// Gauge collects multiple individual gauges and treats them as a unit.\ntype Gauge []metrics.Gauge\n\n// NewGauge returns a multi-gauge, wrapping the passed gauges.\nfunc NewGauge(g ...metrics.Gauge) Gauge {\n\treturn Gauge(g)\n}\n\n// Set implements Gauge.\nfunc (g Gauge) Set(value float64) {\n\tfor _, gauge := range g {\n\t\tgauge.Set(value)\n\t}\n}\n\n// With implements gauge.\nfunc (g Gauge) With(labelValues ...string) metrics.Gauge {\n\tnext := make(Gauge, len(g))\n\tfor i := range g {\n\t\tnext[i] = g[i].With(labelValues...)\n\t}\n\treturn next\n}\n\n// Add implements metrics.Gauge.\nfunc (g Gauge) Add(delta float64) {\n\tfor _, gauge := range g {\n\t\tgauge.Add(delta)\n\t}\n}\n\n// Histogram collects multiple individual histograms and treats them as a unit.\ntype Histogram []metrics.Histogram\n\n// NewHistogram returns a multi-histogram, wrapping the passed histograms.\nfunc NewHistogram(h ...metrics.Histogram) Histogram {\n\treturn Histogram(h)\n}\n\n// Observe implements Histogram.\nfunc (h Histogram) Observe(value float64) {\n\tfor _, histogram := range h {\n\t\thistogram.Observe(value)\n\t}\n}\n\n// With implements histogram.\nfunc (h Histogram) With(labelValues ...string) metrics.Histogram {\n\tnext := make(Histogram, len(h))\n\tfor i := range h {\n\t\tnext[i] = h[i].With(labelValues...)\n\t}\n\treturn next\n}\n"
  },
  {
    "path": "metrics/multi/multi_test.go",
    "content": "package multi\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/go-kit/kit/metrics\"\n)\n\nfunc TestMultiCounter(t *testing.T) {\n\tc1 := &mockCounter{}\n\tc2 := &mockCounter{}\n\tc3 := &mockCounter{}\n\tmc := NewCounter(c1, c2, c3)\n\n\tmc.Add(123)\n\tmc.Add(456)\n\n\twant := \"[123 456]\"\n\tfor i, m := range []fmt.Stringer{c1, c2, c3} {\n\t\tif have := m.String(); want != have {\n\t\t\tt.Errorf(\"c%d: want %q, have %q\", i+1, want, have)\n\t\t}\n\t}\n}\n\nfunc TestMultiGauge(t *testing.T) {\n\tg1 := &mockGauge{}\n\tg2 := &mockGauge{}\n\tg3 := &mockGauge{}\n\tmg := NewGauge(g1, g2, g3)\n\n\tmg.Set(9)\n\tmg.Set(8)\n\tmg.Set(7)\n\tmg.Add(3)\n\n\twant := \"[9 8 7 10]\"\n\tfor i, m := range []fmt.Stringer{g1, g2, g3} {\n\t\tif have := m.String(); want != have {\n\t\t\tt.Errorf(\"g%d: want %q, have %q\", i+1, want, have)\n\t\t}\n\t}\n}\n\nfunc TestMultiHistogram(t *testing.T) {\n\th1 := &mockHistogram{}\n\th2 := &mockHistogram{}\n\th3 := &mockHistogram{}\n\tmh := NewHistogram(h1, h2, h3)\n\n\tmh.Observe(1)\n\tmh.Observe(2)\n\tmh.Observe(4)\n\tmh.Observe(8)\n\n\twant := \"[1 2 4 8]\"\n\tfor i, m := range []fmt.Stringer{h1, h2, h3} {\n\t\tif have := m.String(); want != have {\n\t\t\tt.Errorf(\"g%d: want %q, have %q\", i+1, want, have)\n\t\t}\n\t}\n}\n\ntype mockCounter struct {\n\tobs []float64\n}\n\nfunc (c *mockCounter) Add(delta float64)              { c.obs = append(c.obs, delta) }\nfunc (c *mockCounter) With(...string) metrics.Counter { return c }\nfunc (c *mockCounter) String() string                 { return fmt.Sprintf(\"%v\", c.obs) }\n\ntype mockGauge struct {\n\tobs []float64\n}\n\nfunc (g *mockGauge) Set(value float64)            { g.obs = append(g.obs, value) }\nfunc (g *mockGauge) With(...string) metrics.Gauge { return g }\nfunc (g *mockGauge) String() string               { return fmt.Sprintf(\"%v\", g.obs) }\nfunc (g *mockGauge) Add(delta float64) {\n\tvar value float64\n\tif len(g.obs) > 0 {\n\t\tvalue = g.obs[len(g.obs)-1] + delta\n\t} else {\n\t\tvalue = delta\n\t}\n\tg.obs = append(g.obs, value)\n}\n\ntype mockHistogram struct {\n\tobs []float64\n}\n\nfunc (h *mockHistogram) Observe(value float64)            { h.obs = append(h.obs, value) }\nfunc (h *mockHistogram) With(...string) metrics.Histogram { return h }\nfunc (h *mockHistogram) String() string                   { return fmt.Sprintf(\"%v\", h.obs) }\n"
  },
  {
    "path": "metrics/pcp/pcp.go",
    "content": "package pcp\n\nimport (\n\t\"github.com/performancecopilot/speed/v4\"\n\n\t\"github.com/go-kit/kit/metrics\"\n)\n\n// Reporter encapsulates a speed client.\ntype Reporter struct {\n\tc *speed.PCPClient\n}\n\n// NewReporter creates a new Reporter instance. The first parameter is the\n// application name and is used to create the speed client. Hence it should be a\n// valid speed parameter name and should not contain spaces or the path\n// separator for your operating system.\nfunc NewReporter(appname string) (*Reporter, error) {\n\tc, err := speed.NewPCPClient(appname)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Reporter{c}, nil\n}\n\n// Start starts the underlying speed client so it can start reporting registered\n// metrics to your PCP installation.\nfunc (r *Reporter) Start() { r.c.MustStart() }\n\n// Stop stops the underlying speed client so it can stop reporting registered\n// metrics to your PCP installation.\nfunc (r *Reporter) Stop() { r.c.MustStop() }\n\n// Counter implements metrics.Counter via a single dimensional speed.Counter.\ntype Counter struct {\n\tc speed.Counter\n}\n\n// NewCounter creates a new Counter. This requires a name parameter and can\n// optionally take a couple of description strings, that are used to create the\n// underlying speed.Counter and are reported by PCP.\nfunc (r *Reporter) NewCounter(name string, desc ...string) (*Counter, error) {\n\tc, err := speed.NewPCPCounter(0, name, desc...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tr.c.MustRegister(c)\n\treturn &Counter{c}, nil\n}\n\n// With is a no-op.\nfunc (c *Counter) With(labelValues ...string) metrics.Counter { return c }\n\n// Add increments Counter. speed.Counters only take int64, so delta is converted\n// to int64 before observation.\nfunc (c *Counter) Add(delta float64) { c.c.Inc(int64(delta)) }\n\n// Gauge implements metrics.Gauge via a single dimensional speed.Gauge.\ntype Gauge struct {\n\tg speed.Gauge\n}\n\n// NewGauge creates a new Gauge. This requires a name parameter and can\n// optionally take a couple of description strings, that are used to create the\n// underlying speed.Gauge and are reported by PCP.\nfunc (r *Reporter) NewGauge(name string, desc ...string) (*Gauge, error) {\n\tg, err := speed.NewPCPGauge(0, name, desc...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tr.c.MustRegister(g)\n\treturn &Gauge{g}, nil\n}\n\n// With is a no-op.\nfunc (g *Gauge) With(labelValues ...string) metrics.Gauge { return g }\n\n// Set sets the value of the gauge.\nfunc (g *Gauge) Set(value float64) { g.g.Set(value) }\n\n// Add adds a value to the gauge.\nfunc (g *Gauge) Add(delta float64) { g.g.Inc(delta) }\n\n// Histogram wraps a speed Histogram.\ntype Histogram struct {\n\th speed.Histogram\n}\n\n// NewHistogram creates a new Histogram. The minimum observeable value is 0. The\n// maximum observeable value is 3600000000 (3.6e9).\n//\n// The required parameters are a metric name, the minimum and maximum observable\n// values, and a metric unit for the units of the observed values.\n//\n// Optionally, it can also take a couple of description strings.\nfunc (r *Reporter) NewHistogram(name string, min, max int64, unit speed.MetricUnit, desc ...string) (*Histogram, error) {\n\th, err := speed.NewPCPHistogram(name, min, max, 5, unit, desc...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tr.c.MustRegister(h)\n\treturn &Histogram{h}, nil\n}\n\n// With is a no-op.\nfunc (h *Histogram) With(labelValues ...string) metrics.Histogram { return h }\n\n// Observe observes a value.\n//\n// This converts float64 value to int64 before observation, as the Histogram in\n// speed is backed using codahale/hdrhistogram, which only observes int64\n// values. Additionally, the value is interpreted in the metric unit used to\n// construct the histogram.\nfunc (h *Histogram) Observe(value float64) { h.h.MustRecord(int64(value)) }\n\n// Mean returns the mean of the values observed so far by the Histogram.\nfunc (h *Histogram) Mean() float64 { return h.h.Mean() }\n\n// Percentile returns a percentile value for the given percentile\n// between 0 and 100 for all values observed by the histogram.\nfunc (h *Histogram) Percentile(p float64) int64 { return h.h.Percentile(p) }\n"
  },
  {
    "path": "metrics/pcp/pcp_test.go",
    "content": "package pcp\n\nimport (\n\t\"testing\"\n\n\t\"github.com/performancecopilot/speed/v4\"\n\n\t\"github.com/go-kit/kit/metrics/teststat\"\n)\n\nfunc TestCounter(t *testing.T) {\n\tr, err := NewReporter(\"test_counter\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tcounter, err := r.NewCounter(\"speed_counter\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tcounter = counter.With(\"label values\", \"not supported\").(*Counter)\n\n\tvalue := func() float64 { f := counter.c.Val(); return float64(f) }\n\tif err := teststat.TestCounter(counter, value); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestGauge(t *testing.T) {\n\tr, err := NewReporter(\"test_gauge\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tgauge, err := r.NewGauge(\"speed_gauge\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tgauge = gauge.With(\"label values\", \"not supported\").(*Gauge)\n\n\tvalue := func() []float64 { f := gauge.g.Val(); return []float64{f} }\n\tif err := teststat.TestGauge(gauge, value); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestHistogram(t *testing.T) {\n\tr, err := NewReporter(\"test_histogram\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\thistogram, err := r.NewHistogram(\"speed_histogram\", 0, 3600000000, speed.OneUnit)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\thistogram = histogram.With(\"label values\", \"not supported\").(*Histogram)\n\n\tquantiles := func() (float64, float64, float64, float64) {\n\t\tp50 := float64(histogram.Percentile(50))\n\t\tp90 := float64(histogram.Percentile(90))\n\t\tp95 := float64(histogram.Percentile(95))\n\t\tp99 := float64(histogram.Percentile(99))\n\t\treturn p50, p90, p95, p99\n\t}\n\tif err := teststat.TestHistogram(histogram, quantiles, 0.01); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "metrics/prometheus/prometheus.go",
    "content": "// Package prometheus provides Prometheus implementations for metrics.\n// Individual metrics are mapped to their Prometheus counterparts, and\n// (depending on the constructor used) may be automatically registered in the\n// global Prometheus metrics registry.\npackage prometheus\n\nimport (\n\t\"github.com/prometheus/client_golang/prometheus\"\n\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/internal/lv\"\n)\n\n// Counter implements Counter, via a Prometheus CounterVec.\ntype Counter struct {\n\tcv  *prometheus.CounterVec\n\tlvs lv.LabelValues\n}\n\n// NewCounterFrom constructs and registers a Prometheus CounterVec,\n// and returns a usable Counter object.\nfunc NewCounterFrom(opts prometheus.CounterOpts, labelNames []string) *Counter {\n\tcv := prometheus.NewCounterVec(opts, labelNames)\n\tprometheus.MustRegister(cv)\n\treturn NewCounter(cv)\n}\n\n// NewCounter wraps the CounterVec and returns a usable Counter object.\nfunc NewCounter(cv *prometheus.CounterVec) *Counter {\n\treturn &Counter{\n\t\tcv: cv,\n\t}\n}\n\n// With implements Counter.\nfunc (c *Counter) With(labelValues ...string) metrics.Counter {\n\treturn &Counter{\n\t\tcv:  c.cv,\n\t\tlvs: c.lvs.With(labelValues...),\n\t}\n}\n\n// Add implements Counter.\nfunc (c *Counter) Add(delta float64) {\n\tc.cv.With(makeLabels(c.lvs...)).Add(delta)\n}\n\n// Gauge implements Gauge, via a Prometheus GaugeVec.\ntype Gauge struct {\n\tgv  *prometheus.GaugeVec\n\tlvs lv.LabelValues\n}\n\n// NewGaugeFrom constructs and registers a Prometheus GaugeVec,\n// and returns a usable Gauge object.\nfunc NewGaugeFrom(opts prometheus.GaugeOpts, labelNames []string) *Gauge {\n\tgv := prometheus.NewGaugeVec(opts, labelNames)\n\tprometheus.MustRegister(gv)\n\treturn NewGauge(gv)\n}\n\n// NewGauge wraps the GaugeVec and returns a usable Gauge object.\nfunc NewGauge(gv *prometheus.GaugeVec) *Gauge {\n\treturn &Gauge{\n\t\tgv: gv,\n\t}\n}\n\n// With implements Gauge.\nfunc (g *Gauge) With(labelValues ...string) metrics.Gauge {\n\treturn &Gauge{\n\t\tgv:  g.gv,\n\t\tlvs: g.lvs.With(labelValues...),\n\t}\n}\n\n// Set implements Gauge.\nfunc (g *Gauge) Set(value float64) {\n\tg.gv.With(makeLabels(g.lvs...)).Set(value)\n}\n\n// Add is supported by Prometheus GaugeVecs.\nfunc (g *Gauge) Add(delta float64) {\n\tg.gv.With(makeLabels(g.lvs...)).Add(delta)\n}\n\n// Summary implements Histogram, via a Prometheus SummaryVec. The difference\n// between a Summary and a Histogram is that Summaries don't require predefined\n// quantile buckets, but cannot be statistically aggregated.\ntype Summary struct {\n\tsv  *prometheus.SummaryVec\n\tlvs lv.LabelValues\n}\n\n// NewSummaryFrom constructs and registers a Prometheus SummaryVec,\n// and returns a usable Summary object.\nfunc NewSummaryFrom(opts prometheus.SummaryOpts, labelNames []string) *Summary {\n\tsv := prometheus.NewSummaryVec(opts, labelNames)\n\tprometheus.MustRegister(sv)\n\treturn NewSummary(sv)\n}\n\n// NewSummary wraps the SummaryVec and returns a usable Summary object.\nfunc NewSummary(sv *prometheus.SummaryVec) *Summary {\n\treturn &Summary{\n\t\tsv: sv,\n\t}\n}\n\n// With implements Histogram.\nfunc (s *Summary) With(labelValues ...string) metrics.Histogram {\n\treturn &Summary{\n\t\tsv:  s.sv,\n\t\tlvs: s.lvs.With(labelValues...),\n\t}\n}\n\n// Observe implements Histogram.\nfunc (s *Summary) Observe(value float64) {\n\ts.sv.With(makeLabels(s.lvs...)).Observe(value)\n}\n\n// Histogram implements Histogram via a Prometheus HistogramVec. The difference\n// between a Histogram and a Summary is that Histograms require predefined\n// quantile buckets, and can be statistically aggregated.\ntype Histogram struct {\n\thv  *prometheus.HistogramVec\n\tlvs lv.LabelValues\n}\n\n// NewHistogramFrom constructs and registers a Prometheus HistogramVec,\n// and returns a usable Histogram object.\nfunc NewHistogramFrom(opts prometheus.HistogramOpts, labelNames []string) *Histogram {\n\thv := prometheus.NewHistogramVec(opts, labelNames)\n\tprometheus.MustRegister(hv)\n\treturn NewHistogram(hv)\n}\n\n// NewHistogram wraps the HistogramVec and returns a usable Histogram object.\nfunc NewHistogram(hv *prometheus.HistogramVec) *Histogram {\n\treturn &Histogram{\n\t\thv: hv,\n\t}\n}\n\n// With implements Histogram.\nfunc (h *Histogram) With(labelValues ...string) metrics.Histogram {\n\treturn &Histogram{\n\t\thv:  h.hv,\n\t\tlvs: h.lvs.With(labelValues...),\n\t}\n}\n\n// Observe implements Histogram.\nfunc (h *Histogram) Observe(value float64) {\n\th.hv.With(makeLabels(h.lvs...)).Observe(value)\n}\n\nfunc makeLabels(labelValues ...string) prometheus.Labels {\n\tlabels := prometheus.Labels{}\n\tfor i := 0; i < len(labelValues); i += 2 {\n\t\tlabels[labelValues[i]] = labelValues[i+1]\n\t}\n\treturn labels\n}\n"
  },
  {
    "path": "metrics/prometheus/prometheus_test.go",
    "content": "package prometheus\n\nimport (\n\t\"io/ioutil\"\n\t\"math\"\n\t\"math/rand\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/go-kit/kit/metrics/teststat\"\n\tstdprometheus \"github.com/prometheus/client_golang/prometheus\"\n\t\"github.com/prometheus/client_golang/prometheus/promhttp\"\n)\n\nfunc TestCounter(t *testing.T) {\n\ts := httptest.NewServer(promhttp.HandlerFor(stdprometheus.DefaultGatherer, promhttp.HandlerOpts{}))\n\tdefer s.Close()\n\n\tscrape := func() string {\n\t\tresp, _ := http.Get(s.URL)\n\t\tbuf, _ := ioutil.ReadAll(resp.Body)\n\t\treturn string(buf)\n\t}\n\n\tnamespace, subsystem, name := \"ns\", \"ss\", \"foo\"\n\tre := regexp.MustCompile(namespace + `_` + subsystem + `_` + name + `{alpha=\"alpha-value\",beta=\"beta-value\"} ([0-9\\.]+)`)\n\n\tcounter := NewCounterFrom(stdprometheus.CounterOpts{\n\t\tNamespace: namespace,\n\t\tSubsystem: subsystem,\n\t\tName:      name,\n\t\tHelp:      \"This is the help string.\",\n\t}, []string{\"alpha\", \"beta\"}).With(\"beta\", \"beta-value\", \"alpha\", \"alpha-value\") // order shouldn't matter\n\n\tvalue := func() float64 {\n\t\tmatches := re.FindStringSubmatch(scrape())\n\t\tf, _ := strconv.ParseFloat(matches[1], 64)\n\t\treturn f\n\t}\n\n\tif err := teststat.TestCounter(counter, value); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestGauge(t *testing.T) {\n\ts := httptest.NewServer(promhttp.HandlerFor(stdprometheus.DefaultGatherer, promhttp.HandlerOpts{}))\n\tdefer s.Close()\n\n\tscrape := func() string {\n\t\tresp, _ := http.Get(s.URL)\n\t\tbuf, _ := ioutil.ReadAll(resp.Body)\n\t\treturn string(buf)\n\t}\n\n\tnamespace, subsystem, name := \"aaa\", \"bbb\", \"ccc\"\n\tre := regexp.MustCompile(namespace + `_` + subsystem + `_` + name + `{foo=\"bar\"} ([0-9\\.]+)`)\n\n\tgauge := NewGaugeFrom(stdprometheus.GaugeOpts{\n\t\tNamespace: namespace,\n\t\tSubsystem: subsystem,\n\t\tName:      name,\n\t\tHelp:      \"This is a different help string.\",\n\t}, []string{\"foo\"}).With(\"foo\", \"bar\")\n\n\tvalue := func() []float64 {\n\t\tmatches := re.FindStringSubmatch(scrape())\n\t\tf, _ := strconv.ParseFloat(matches[1], 64)\n\t\treturn []float64{f}\n\t}\n\n\tif err := teststat.TestGauge(gauge, value); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestSummary(t *testing.T) {\n\ts := httptest.NewServer(promhttp.HandlerFor(stdprometheus.DefaultGatherer, promhttp.HandlerOpts{}))\n\tdefer s.Close()\n\n\tscrape := func() string {\n\t\tresp, _ := http.Get(s.URL)\n\t\tbuf, _ := ioutil.ReadAll(resp.Body)\n\t\treturn string(buf)\n\t}\n\n\tnamespace, subsystem, name := \"test\", \"prometheus\", \"summary\"\n\tre50 := regexp.MustCompile(namespace + `_` + subsystem + `_` + name + `{a=\"a\",b=\"b\",quantile=\"0.5\"} ([0-9\\.]+)`)\n\tre90 := regexp.MustCompile(namespace + `_` + subsystem + `_` + name + `{a=\"a\",b=\"b\",quantile=\"0.9\"} ([0-9\\.]+)`)\n\tre99 := regexp.MustCompile(namespace + `_` + subsystem + `_` + name + `{a=\"a\",b=\"b\",quantile=\"0.99\"} ([0-9\\.]+)`)\n\n\tsummary := NewSummaryFrom(stdprometheus.SummaryOpts{\n\t\tNamespace:  namespace,\n\t\tSubsystem:  subsystem,\n\t\tName:       name,\n\t\tHelp:       \"This is the help string for the summary.\",\n\t\tObjectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},\n\t}, []string{\"a\", \"b\"}).With(\"b\", \"b\").With(\"a\", \"a\")\n\n\tquantiles := func() (float64, float64, float64, float64) {\n\t\tbuf := scrape()\n\t\tmatch50 := re50.FindStringSubmatch(buf)\n\t\tp50, _ := strconv.ParseFloat(match50[1], 64)\n\t\tmatch90 := re90.FindStringSubmatch(buf)\n\t\tp90, _ := strconv.ParseFloat(match90[1], 64)\n\t\tmatch99 := re99.FindStringSubmatch(buf)\n\t\tp99, _ := strconv.ParseFloat(match99[1], 64)\n\t\tp95 := p90 + ((p99 - p90) / 2) // Prometheus, y u no p95??? :< #yolo\n\t\treturn p50, p90, p95, p99\n\t}\n\n\tif err := teststat.TestHistogram(summary, quantiles, 0.01); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestHistogram(t *testing.T) {\n\t// Prometheus reports histograms as a count of observations that fell into\n\t// each predefined bucket, with the bucket value representing a global upper\n\t// limit. That is, the count monotonically increases over the buckets. This\n\t// requires a different strategy to test.\n\n\ts := httptest.NewServer(promhttp.HandlerFor(stdprometheus.DefaultGatherer, promhttp.HandlerOpts{}))\n\tdefer s.Close()\n\n\tscrape := func() string {\n\t\tresp, _ := http.Get(s.URL)\n\t\tbuf, _ := ioutil.ReadAll(resp.Body)\n\t\treturn string(buf)\n\t}\n\n\tnamespace, subsystem, name := \"test\", \"prometheus\", \"histogram\"\n\tre := regexp.MustCompile(namespace + `_` + subsystem + `_` + name + `_bucket{x=\"1\",le=\"([0-9]+|\\+Inf)\"} ([0-9\\.]+)`)\n\n\tnumStdev := 3\n\tbucketMin := (teststat.Mean - (numStdev * teststat.Stdev))\n\tbucketMax := (teststat.Mean + (numStdev * teststat.Stdev))\n\tif bucketMin < 0 {\n\t\tbucketMin = 0\n\t}\n\tbucketCount := 10\n\tbucketDelta := (bucketMax - bucketMin) / bucketCount\n\tbuckets := []float64{}\n\tfor i := bucketMin; i <= bucketMax; i += bucketDelta {\n\t\tbuckets = append(buckets, float64(i))\n\t}\n\n\thistogram := NewHistogramFrom(stdprometheus.HistogramOpts{\n\t\tNamespace: namespace,\n\t\tSubsystem: subsystem,\n\t\tName:      name,\n\t\tHelp:      \"This is the help string for the histogram.\",\n\t\tBuckets:   buckets,\n\t}, []string{\"x\"}).With(\"x\", \"1\")\n\n\t// Can't TestHistogram, because Prometheus Histograms don't dynamically\n\t// compute quantiles. Instead, they fill up buckets. So, let's populate the\n\t// histogram kind of manually.\n\tteststat.PopulateNormalHistogram(histogram, rand.Int())\n\n\t// Then, we use ExpectedObservationsLessThan to validate.\n\tfor _, line := range strings.Split(scrape(), \"\\n\") {\n\t\tmatch := re.FindStringSubmatch(line)\n\t\tif match == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tbucket, _ := strconv.ParseInt(match[1], 10, 64)\n\t\thave, _ := strconv.ParseFloat(match[2], 64)\n\n\t\twant := teststat.ExpectedObservationsLessThan(bucket)\n\t\tif match[1] == \"+Inf\" {\n\t\t\twant = int64(teststat.Count) // special case\n\t\t}\n\n\t\t// Unfortunately, we observe experimentally that Prometheus is quite\n\t\t// imprecise at the extremes. I'm setting a very high tolerance for now.\n\t\t// It would be great to dig in and figure out whether that's a problem\n\t\t// with my Expected calculation, or in Prometheus.\n\t\ttolerance := 0.25\n\t\tif delta := math.Abs(float64(want) - float64(have)); (delta / float64(want)) > tolerance {\n\t\t\tt.Errorf(\"Bucket %d: want %d, have %d (%.1f%%)\", bucket, want, int(have), (100.0 * delta / float64(want)))\n\t\t}\n\t}\n}\n\nfunc TestInconsistentLabelCardinality(t *testing.T) {\n\tdefer func() {\n\t\tx := recover()\n\t\tif x == nil {\n\t\t\tt.Fatal(\"expected panic, got none\")\n\t\t}\n\t\terr, ok := x.(error)\n\t\tif !ok {\n\t\t\tt.Fatalf(\"expected error, got %s\", reflect.TypeOf(x))\n\t\t}\n\t\tif want, have := \"inconsistent label cardinality\", err.Error(); !strings.HasPrefix(have, want) {\n\t\t\tt.Fatalf(\"want %q, have %q\", want, have)\n\t\t}\n\t}()\n\n\tNewCounterFrom(stdprometheus.CounterOpts{\n\t\tNamespace: \"test\",\n\t\tSubsystem: \"inconsistent_label_cardinality\",\n\t\tName:      \"foobar\",\n\t\tHelp:      \"This is the help string for the metric.\",\n\t}, []string{\"a\", \"b\"}).With(\n\t\t\"a\", \"1\", \"b\", \"2\", \"c\", \"KABOOM!\",\n\t).Add(123)\n}\n"
  },
  {
    "path": "metrics/provider/discard.go",
    "content": "package provider\n\nimport (\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/discard\"\n)\n\ntype discardProvider struct{}\n\n// NewDiscardProvider returns a provider that produces no-op metrics via the\n// discarding backend.\nfunc NewDiscardProvider() Provider { return discardProvider{} }\n\n// NewCounter implements Provider.\nfunc (discardProvider) NewCounter(string) metrics.Counter { return discard.NewCounter() }\n\n// NewGauge implements Provider.\nfunc (discardProvider) NewGauge(string) metrics.Gauge { return discard.NewGauge() }\n\n// NewHistogram implements Provider.\nfunc (discardProvider) NewHistogram(string, int) metrics.Histogram { return discard.NewHistogram() }\n\n// Stop implements Provider.\nfunc (discardProvider) Stop() {}\n"
  },
  {
    "path": "metrics/provider/dogstatsd.go",
    "content": "package provider\n\nimport (\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/dogstatsd\"\n)\n\ntype dogstatsdProvider struct {\n\td    *dogstatsd.Dogstatsd\n\tstop func()\n}\n\n// NewDogstatsdProvider wraps the given Dogstatsd object and stop func and\n// returns a Provider that produces Dogstatsd metrics. A typical stop function\n// would be ticker.Stop from the ticker passed to the SendLoop helper method.\nfunc NewDogstatsdProvider(d *dogstatsd.Dogstatsd, stop func()) Provider {\n\treturn &dogstatsdProvider{\n\t\td:    d,\n\t\tstop: stop,\n\t}\n}\n\n// NewCounter implements Provider, returning a new Dogstatsd Counter with a\n// sample rate of 1.0.\nfunc (p *dogstatsdProvider) NewCounter(name string) metrics.Counter {\n\treturn p.d.NewCounter(name, 1.0)\n}\n\n// NewGauge implements Provider.\nfunc (p *dogstatsdProvider) NewGauge(name string) metrics.Gauge {\n\treturn p.d.NewGauge(name)\n}\n\n// NewHistogram implements Provider, returning a new Dogstatsd Histogram (note:\n// not a Timing) with a sample rate of 1.0. The buckets argument is ignored.\nfunc (p *dogstatsdProvider) NewHistogram(name string, _ int) metrics.Histogram {\n\treturn p.d.NewHistogram(name, 1.0)\n}\n\n// Stop implements Provider, invoking the stop function passed at construction.\nfunc (p *dogstatsdProvider) Stop() {\n\tp.stop()\n}\n"
  },
  {
    "path": "metrics/provider/expvar.go",
    "content": "package provider\n\nimport (\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/expvar\"\n)\n\ntype expvarProvider struct{}\n\n// NewExpvarProvider returns a Provider that produces expvar metrics.\nfunc NewExpvarProvider() Provider {\n\treturn expvarProvider{}\n}\n\n// NewCounter implements Provider.\nfunc (p expvarProvider) NewCounter(name string) metrics.Counter {\n\treturn expvar.NewCounter(name)\n}\n\n// NewGauge implements Provider.\nfunc (p expvarProvider) NewGauge(name string) metrics.Gauge {\n\treturn expvar.NewGauge(name)\n}\n\n// NewHistogram implements Provider.\nfunc (p expvarProvider) NewHistogram(name string, buckets int) metrics.Histogram {\n\treturn expvar.NewHistogram(name, buckets)\n}\n\n// Stop implements Provider, but is a no-op.\nfunc (p expvarProvider) Stop() {}\n"
  },
  {
    "path": "metrics/provider/graphite.go",
    "content": "package provider\n\nimport (\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/graphite\"\n)\n\ntype graphiteProvider struct {\n\tg    *graphite.Graphite\n\tstop func()\n}\n\n// NewGraphiteProvider wraps the given Graphite object and stop func and returns\n// a Provider that produces Graphite metrics. A typical stop function would be\n// ticker.Stop from the ticker passed to the SendLoop helper method.\nfunc NewGraphiteProvider(g *graphite.Graphite, stop func()) Provider {\n\treturn &graphiteProvider{\n\t\tg:    g,\n\t\tstop: stop,\n\t}\n}\n\n// NewCounter implements Provider.\nfunc (p *graphiteProvider) NewCounter(name string) metrics.Counter {\n\treturn p.g.NewCounter(name)\n}\n\n// NewGauge implements Provider.\nfunc (p *graphiteProvider) NewGauge(name string) metrics.Gauge {\n\treturn p.g.NewGauge(name)\n}\n\n// NewHistogram implements Provider.\nfunc (p *graphiteProvider) NewHistogram(name string, buckets int) metrics.Histogram {\n\treturn p.g.NewHistogram(name, buckets)\n}\n\n// Stop implements Provider, invoking the stop function passed at construction.\nfunc (p *graphiteProvider) Stop() {\n\tp.stop()\n}\n"
  },
  {
    "path": "metrics/provider/influx.go",
    "content": "package provider\n\nimport (\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/influx\"\n)\n\ntype influxProvider struct {\n\tin   *influx.Influx\n\tstop func()\n}\n\n// NewInfluxProvider takes the given Influx object and stop func, and returns\n// a Provider that produces Influx metrics.\nfunc NewInfluxProvider(in *influx.Influx, stop func()) Provider {\n\treturn &influxProvider{\n\t\tin:   in,\n\t\tstop: stop,\n\t}\n}\n\n// NewCounter implements Provider. Per-metric tags are not supported.\nfunc (p *influxProvider) NewCounter(name string) metrics.Counter {\n\treturn p.in.NewCounter(name)\n}\n\n// NewGauge implements Provider. Per-metric tags are not supported.\nfunc (p *influxProvider) NewGauge(name string) metrics.Gauge {\n\treturn p.in.NewGauge(name)\n}\n\n// NewHistogram implements Provider. Per-metric tags are not supported.\nfunc (p *influxProvider) NewHistogram(name string, buckets int) metrics.Histogram {\n\treturn p.in.NewHistogram(name)\n}\n\n// Stop implements Provider, invoking the stop function passed at construction.\nfunc (p *influxProvider) Stop() {\n\tp.stop()\n}\n"
  },
  {
    "path": "metrics/provider/prometheus.go",
    "content": "package provider\n\nimport (\n\tstdprometheus \"github.com/prometheus/client_golang/prometheus\"\n\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/prometheus\"\n)\n\ntype prometheusProvider struct {\n\tnamespace string\n\tsubsystem string\n}\n\n// NewPrometheusProvider returns a Provider that produces Prometheus metrics.\n// Namespace and subsystem are applied to all produced metrics.\nfunc NewPrometheusProvider(namespace, subsystem string) Provider {\n\treturn &prometheusProvider{\n\t\tnamespace: namespace,\n\t\tsubsystem: subsystem,\n\t}\n}\n\n// NewCounter implements Provider via prometheus.NewCounterFrom, i.e. the\n// counter is registered. The metric's namespace and subsystem are taken from\n// the Provider. Help is set to the name of the metric, and no const label names\n// are set.\nfunc (p *prometheusProvider) NewCounter(name string) metrics.Counter {\n\treturn prometheus.NewCounterFrom(stdprometheus.CounterOpts{\n\t\tNamespace: p.namespace,\n\t\tSubsystem: p.subsystem,\n\t\tName:      name,\n\t\tHelp:      name,\n\t}, []string{})\n}\n\n// NewGauge implements Provider via prometheus.NewGaugeFrom, i.e. the gauge is\n// registered. The metric's namespace and subsystem are taken from the Provider.\n// Help is set to the name of the metric, and no const label names are set.\nfunc (p *prometheusProvider) NewGauge(name string) metrics.Gauge {\n\treturn prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{\n\t\tNamespace: p.namespace,\n\t\tSubsystem: p.subsystem,\n\t\tName:      name,\n\t\tHelp:      name,\n\t}, []string{})\n}\n\n// NewHistogram implements Provider via prometheus.NewSummaryFrom, i.e. the summary\n// is registered. The metric's namespace and subsystem are taken from the\n// Provider. Help is set to the name of the metric, and no const label names are\n// set. Buckets are ignored.\nfunc (p *prometheusProvider) NewHistogram(name string, _ int) metrics.Histogram {\n\treturn prometheus.NewSummaryFrom(stdprometheus.SummaryOpts{\n\t\tNamespace: p.namespace,\n\t\tSubsystem: p.subsystem,\n\t\tName:      name,\n\t\tHelp:      name,\n\t}, []string{})\n}\n\n// Stop implements Provider, but is a no-op.\nfunc (p *prometheusProvider) Stop() {}\n"
  },
  {
    "path": "metrics/provider/provider.go",
    "content": "// Package provider provides a factory-like abstraction for metrics backends.\n// This package is provided specifically for the needs of the NY Times framework\n// Gizmo. Most normal Go kit users shouldn't need to use it.\n//\n// Normally, if your microservice needs to support different metrics backends,\n// you can simply do different construction based on a flag. For example,\n//\n//    var latency metrics.Histogram\n//    var requests metrics.Counter\n//    switch *metricsBackend {\n//    case \"prometheus\":\n//        latency = prometheus.NewSummaryVec(...)\n//        requests = prometheus.NewCounterVec(...)\n//    case \"statsd\":\n//        s := statsd.New(...)\n//        t := time.NewTicker(5*time.Second)\n//        go s.SendLoop(ctx, t.C, \"tcp\", \"statsd.local:8125\")\n//        latency = s.NewHistogram(...)\n//        requests = s.NewCounter(...)\n//    default:\n//        log.Fatal(\"unsupported metrics backend %q\", *metricsBackend)\n//    }\n//\npackage provider\n\nimport (\n\t\"github.com/go-kit/kit/metrics\"\n)\n\n// Provider abstracts over constructors and lifecycle management functions for\n// each supported metrics backend. It should only be used by those who need to\n// swap out implementations dynamically.\n//\n// This is primarily useful for intermediating frameworks, and is likely\n// unnecessary for most Go kit services. See the package-level doc comment for\n// more typical usage instructions.\ntype Provider interface {\n\tNewCounter(name string) metrics.Counter\n\tNewGauge(name string) metrics.Gauge\n\tNewHistogram(name string, buckets int) metrics.Histogram\n\tStop()\n}\n"
  },
  {
    "path": "metrics/provider/statsd.go",
    "content": "package provider\n\nimport (\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/statsd\"\n)\n\ntype statsdProvider struct {\n\ts    *statsd.Statsd\n\tstop func()\n}\n\n// NewStatsdProvider wraps the given Statsd object and stop func and returns a\n// Provider that produces Statsd metrics. A typical stop function would be\n// ticker.Stop from the ticker passed to the SendLoop helper method.\nfunc NewStatsdProvider(s *statsd.Statsd, stop func()) Provider {\n\treturn &statsdProvider{\n\t\ts:    s,\n\t\tstop: stop,\n\t}\n}\n\n// NewCounter implements Provider.\nfunc (p *statsdProvider) NewCounter(name string) metrics.Counter {\n\treturn p.s.NewCounter(name, 1.0)\n}\n\n// NewGauge implements Provider.\nfunc (p *statsdProvider) NewGauge(name string) metrics.Gauge {\n\treturn p.s.NewGauge(name)\n}\n\n// NewHistogram implements Provider, returning a StatsD Timing that accepts\n// observations in milliseconds. The sample rate is fixed at 1.0. The bucket\n// parameter is ignored.\nfunc (p *statsdProvider) NewHistogram(name string, _ int) metrics.Histogram {\n\treturn p.s.NewTiming(name, 1.0)\n}\n\n// Stop implements Provider, invoking the stop function passed at construction.\nfunc (p *statsdProvider) Stop() {\n\tp.stop()\n}\n"
  },
  {
    "path": "metrics/statsd/statsd.go",
    "content": "// Package statsd provides a StatsD backend for package metrics. StatsD has no\n// concept of arbitrary key-value tagging, so label values are not supported,\n// and With is a no-op on all metrics.\n//\n// This package batches observations and emits them on some schedule to the\n// remote server. This is useful even if you connect to your StatsD server over\n// UDP. Emitting one network packet per observation can quickly overwhelm even\n// the fastest internal network.\npackage statsd\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/internal/lv\"\n\t\"github.com/go-kit/kit/metrics/internal/ratemap\"\n\t\"github.com/go-kit/kit/util/conn\"\n\t\"github.com/go-kit/log\"\n)\n\n// Statsd receives metrics observations and forwards them to a StatsD server.\n// Create a Statsd object, use it to create metrics, and pass those metrics as\n// dependencies to the components that will use them.\n//\n// All metrics are buffered until WriteTo is called. Counters and gauges are\n// aggregated into a single observation per timeseries per write. Timings are\n// buffered but not aggregated.\n//\n// To regularly report metrics to an io.Writer, use the WriteLoop helper method.\n// To send to a StatsD server, use the SendLoop helper method.\ntype Statsd struct {\n\tprefix string\n\trates  *ratemap.RateMap\n\n\t// The observations are collected in an N-dimensional vector space, even\n\t// though they only take advantage of a single dimension (name). This is an\n\t// implementation detail born purely from convenience. It would be more\n\t// accurate to collect them in a map[string][]float64, but we already have\n\t// this nice data structure and helper methods.\n\tcounters *lv.Space\n\tgauges   *lv.Space\n\ttimings  *lv.Space\n\n\tlogger log.Logger\n}\n\n// New returns a Statsd object that may be used to create metrics. Prefix is\n// applied to all created metrics. Callers must ensure that regular calls to\n// WriteTo are performed, either manually or with one of the helper methods.\nfunc New(prefix string, logger log.Logger) *Statsd {\n\treturn &Statsd{\n\t\tprefix:   prefix,\n\t\trates:    ratemap.New(),\n\t\tcounters: lv.NewSpace(),\n\t\tgauges:   lv.NewSpace(),\n\t\ttimings:  lv.NewSpace(),\n\t\tlogger:   logger,\n\t}\n}\n\n// NewCounter returns a counter, sending observations to this Statsd object.\nfunc (s *Statsd) NewCounter(name string, sampleRate float64) *Counter {\n\ts.rates.Set(s.prefix+name, sampleRate)\n\treturn &Counter{\n\t\tname: s.prefix + name,\n\t\tobs:  s.counters.Observe,\n\t}\n}\n\n// NewGauge returns a gauge, sending observations to this Statsd object.\nfunc (s *Statsd) NewGauge(name string) *Gauge {\n\treturn &Gauge{\n\t\tname: s.prefix + name,\n\t\tobs:  s.gauges.Observe,\n\t\tadd:  s.gauges.Add,\n\t}\n}\n\n// NewTiming returns a histogram whose observations are interpreted as\n// millisecond durations, and are forwarded to this Statsd object.\nfunc (s *Statsd) NewTiming(name string, sampleRate float64) *Timing {\n\ts.rates.Set(s.prefix+name, sampleRate)\n\treturn &Timing{\n\t\tname: s.prefix + name,\n\t\tobs:  s.timings.Observe,\n\t}\n}\n\n// WriteLoop is a helper method that invokes WriteTo to the passed writer every\n// time the passed channel fires. This method blocks until ctx is canceled,\n// so clients probably want to run it in its own goroutine. For typical\n// usage, create a time.Ticker and pass its C channel to this method.\nfunc (s *Statsd) WriteLoop(ctx context.Context, c <-chan time.Time, w io.Writer) {\n\tfor {\n\t\tselect {\n\t\tcase <-c:\n\t\t\tif _, err := s.WriteTo(w); err != nil {\n\t\t\t\ts.logger.Log(\"during\", \"WriteTo\", \"err\", err)\n\t\t\t}\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// SendLoop is a helper method that wraps WriteLoop, passing a managed\n// connection to the network and address. Like WriteLoop, this method blocks\n// until ctx is canceled, so clients probably want to start it in its own\n// goroutine. For typical usage, create a time.Ticker and pass its C channel to\n// this method.\nfunc (s *Statsd) SendLoop(ctx context.Context, c <-chan time.Time, network, address string) {\n\ts.WriteLoop(ctx, c, conn.NewDefaultManager(network, address, s.logger))\n}\n\n// WriteTo flushes the buffered content of the metrics to the writer, in\n// StatsD format. WriteTo abides best-effort semantics, so observations are\n// lost if there is a problem with the write. Clients should be sure to call\n// WriteTo regularly, ideally through the WriteLoop or SendLoop helper methods.\nfunc (s *Statsd) WriteTo(w io.Writer) (count int64, err error) {\n\tvar n int\n\n\ts.counters.Reset().Walk(func(name string, _ lv.LabelValues, values []float64) bool {\n\t\tn, err = fmt.Fprintf(w, \"%s:%f|c%s\\n\", name, sum(values), sampling(s.rates.Get(name)))\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tcount += int64(n)\n\t\treturn true\n\t})\n\tif err != nil {\n\t\treturn count, err\n\t}\n\n\ts.gauges.Reset().Walk(func(name string, _ lv.LabelValues, values []float64) bool {\n\t\tn, err = fmt.Fprintf(w, \"%s:%f|g\\n\", name, last(values))\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tcount += int64(n)\n\t\treturn true\n\t})\n\tif err != nil {\n\t\treturn count, err\n\t}\n\n\ts.timings.Reset().Walk(func(name string, _ lv.LabelValues, values []float64) bool {\n\t\tsampleRate := s.rates.Get(name)\n\t\tfor _, value := range values {\n\t\t\tn, err = fmt.Fprintf(w, \"%s:%f|ms%s\\n\", name, value, sampling(sampleRate))\n\t\t\tif err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tcount += int64(n)\n\t\t}\n\t\treturn true\n\t})\n\tif err != nil {\n\t\treturn count, err\n\t}\n\n\treturn count, err\n}\n\nfunc sum(a []float64) float64 {\n\tvar v float64\n\tfor _, f := range a {\n\t\tv += f\n\t}\n\treturn v\n}\n\nfunc last(a []float64) float64 {\n\treturn a[len(a)-1]\n}\n\nfunc sampling(r float64) string {\n\tvar sv string\n\tif r < 1.0 {\n\t\tsv = fmt.Sprintf(\"|@%f\", r)\n\t}\n\treturn sv\n}\n\ntype observeFunc func(name string, lvs lv.LabelValues, value float64)\n\n// Counter is a StatsD counter. Observations are forwarded to a Statsd object,\n// and aggregated (summed) per timeseries.\ntype Counter struct {\n\tname string\n\tobs  observeFunc\n}\n\n// With is a no-op.\nfunc (c *Counter) With(...string) metrics.Counter {\n\treturn c\n}\n\n// Add implements metrics.Counter.\nfunc (c *Counter) Add(delta float64) {\n\tc.obs(c.name, lv.LabelValues{}, delta)\n}\n\n// Gauge is a StatsD gauge. Observations are forwarded to a Statsd object, and\n// aggregated (the last observation selected) per timeseries.\ntype Gauge struct {\n\tname string\n\tobs  observeFunc\n\tadd  observeFunc\n}\n\n// With is a no-op.\nfunc (g *Gauge) With(...string) metrics.Gauge {\n\treturn g\n}\n\n// Set implements metrics.Gauge.\nfunc (g *Gauge) Set(value float64) {\n\tg.obs(g.name, lv.LabelValues{}, value)\n}\n\n// Add implements metrics.Gauge.\nfunc (g *Gauge) Add(delta float64) {\n\tg.add(g.name, lv.LabelValues{}, delta)\n}\n\n// Timing is a StatsD timing, or metrics.Histogram. Observations are\n// forwarded to a Statsd object, and collected (but not aggregated) per\n// timeseries.\ntype Timing struct {\n\tname string\n\tobs  observeFunc\n}\n\n// With is a no-op.\nfunc (t *Timing) With(...string) metrics.Histogram {\n\treturn t\n}\n\n// Observe implements metrics.Histogram. Value is interpreted as milliseconds.\nfunc (t *Timing) Observe(value float64) {\n\tt.obs(t.name, lv.LabelValues{}, value)\n}\n"
  },
  {
    "path": "metrics/statsd/statsd_test.go",
    "content": "package statsd\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-kit/kit/metrics/teststat\"\n\t\"github.com/go-kit/log\"\n)\n\nfunc TestCounter(t *testing.T) {\n\tprefix, name := \"abc.\", \"def\"\n\tlabel, value := \"label\", \"value\" // ignored\n\tregex := `^` + prefix + name + `:([0-9\\.]+)\\|c$`\n\ts := New(prefix, log.NewNopLogger())\n\tcounter := s.NewCounter(name, 1.0).With(label, value)\n\tvaluef := teststat.SumLines(s, regex)\n\tif err := teststat.TestCounter(counter, valuef); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestCounterSampled(t *testing.T) {\n\t// This will involve multiplying the observed sum by the inverse of the\n\t// sample rate and checking against the expected value within some\n\t// tolerance.\n\tt.Skip(\"TODO\")\n}\n\nfunc TestGauge(t *testing.T) {\n\tprefix, name := \"ghi.\", \"jkl\"\n\tlabel, value := \"xyz\", \"abc\" // ignored\n\tregex := `^` + prefix + name + `:([0-9\\.]+)\\|g$`\n\ts := New(prefix, log.NewNopLogger())\n\tgauge := s.NewGauge(name).With(label, value)\n\tvaluef := teststat.LastLine(s, regex)\n\tif err := teststat.TestGauge(gauge, valuef); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\n// StatsD timings just emit all observations. So, we collect them into a generic\n// histogram, and run the statistics test on that.\n\nfunc TestTiming(t *testing.T) {\n\tprefix, name := \"statsd.\", \"timing_test\"\n\tlabel, value := \"abc\", \"def\" // ignored\n\tregex := `^` + prefix + name + `:([0-9\\.]+)\\|ms$`\n\ts := New(prefix, log.NewNopLogger())\n\ttiming := s.NewTiming(name, 1.0).With(label, value)\n\tquantiles := teststat.Quantiles(s, regex, 50) // no |@0.X\n\tif err := teststat.TestHistogram(timing, quantiles, 0.01); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestTimingSampled(t *testing.T) {\n\tprefix, name := \"statsd.\", \"sampled_timing_test\"\n\tlabel, value := \"foo\", \"bar\" // ignored\n\tregex := `^` + prefix + name + `:([0-9\\.]+)\\|ms\\|@0\\.01[0]*$`\n\ts := New(prefix, log.NewNopLogger())\n\ttiming := s.NewTiming(name, 0.01).With(label, value)\n\tquantiles := teststat.Quantiles(s, regex, 50)\n\tif err := teststat.TestHistogram(timing, quantiles, 0.02); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "metrics/teststat/buffers.go",
    "content": "package teststat\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"io\"\n\t\"regexp\"\n\t\"strconv\"\n\n\t\"github.com/go-kit/kit/metrics/generic\"\n)\n\n// SumLines expects a regex whose first capture group can be parsed as a\n// float64. It will dump the WriterTo and parse each line, expecting to find a\n// match. It returns the sum of all captured floats.\nfunc SumLines(w io.WriterTo, regex string) func() float64 {\n\treturn func() float64 {\n\t\tsum, _ := stats(w, regex, nil)\n\t\treturn sum\n\t}\n}\n\n// LastLine expects a regex whose first capture group can be parsed as a\n// float64. It will dump the WriterTo and parse each line, expecting to find a\n// match. It returns the final captured float.\nfunc LastLine(w io.WriterTo, regex string) func() []float64 {\n\treturn func() []float64 {\n\t\t_, final := stats(w, regex, nil)\n\t\treturn []float64{final}\n\t}\n}\n\n// Quantiles expects a regex whose first capture group can be parsed as a\n// float64. It will dump the WriterTo and parse each line, expecting to find a\n// match. It observes all captured floats into a generic.Histogram with the\n// given number of buckets, and returns the 50th, 90th, 95th, and 99th quantiles\n// from that histogram.\nfunc Quantiles(w io.WriterTo, regex string, buckets int) func() (float64, float64, float64, float64) {\n\treturn func() (float64, float64, float64, float64) {\n\t\th := generic.NewHistogram(\"quantile-test\", buckets)\n\t\tstats(w, regex, h)\n\t\treturn h.Quantile(0.50), h.Quantile(0.90), h.Quantile(0.95), h.Quantile(0.99)\n\t}\n}\n\nfunc stats(w io.WriterTo, regex string, h *generic.Histogram) (sum, final float64) {\n\tre := regexp.MustCompile(regex)\n\tbuf := &bytes.Buffer{}\n\tw.WriteTo(buf)\n\ts := bufio.NewScanner(buf)\n\tfor s.Scan() {\n\t\tmatch := re.FindStringSubmatch(s.Text())\n\t\tf, err := strconv.ParseFloat(match[1], 64)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tsum += f\n\t\tfinal = f\n\t\tif h != nil {\n\t\t\th.Observe(f)\n\t\t}\n\t}\n\treturn sum, final\n}\n"
  },
  {
    "path": "metrics/teststat/populate.go",
    "content": "package teststat\n\nimport (\n\t\"math\"\n\t\"math/rand\"\n\n\t\"github.com/go-kit/kit/metrics\"\n)\n\n// PopulateNormalHistogram makes a series of normal random observations into the\n// histogram. The number of observations is determined by Count. The randomness\n// is determined by Mean, Stdev, and the seed parameter.\n//\n// This is a low-level function, exported only for metrics that don't perform\n// dynamic quantile computation, like a Prometheus Histogram (c.f. Summary). In\n// most cases, you don't need to use this function, and can use TestHistogram\n// instead.\nfunc PopulateNormalHistogram(h metrics.Histogram, seed int) {\n\tr := rand.New(rand.NewSource(int64(seed)))\n\tfor i := 0; i < Count; i++ {\n\t\tsample := r.NormFloat64()*float64(Stdev) + float64(Mean)\n\t\tif sample < 0 {\n\t\t\tsample = 0\n\t\t}\n\t\th.Observe(sample)\n\t}\n}\n\nfunc normalQuantiles() (p50, p90, p95, p99 float64) {\n\treturn nvq(50), nvq(90), nvq(95), nvq(99)\n}\n\nfunc nvq(quantile int) float64 {\n\t// https://en.wikipedia.org/wiki/Normal_distribution#Quantile_function\n\treturn float64(Mean) + float64(Stdev)*math.Sqrt2*erfinv(2*(float64(quantile)/100)-1)\n}\n\nfunc erfinv(y float64) float64 {\n\t// https://stackoverflow.com/questions/5971830/need-code-for-inverse-error-function\n\tif y < -1.0 || y > 1.0 {\n\t\tpanic(\"invalid input\")\n\t}\n\n\tvar (\n\t\ta = [4]float64{0.886226899, -1.645349621, 0.914624893, -0.140543331}\n\t\tb = [4]float64{-2.118377725, 1.442710462, -0.329097515, 0.012229801}\n\t\tc = [4]float64{-1.970840454, -1.624906493, 3.429567803, 1.641345311}\n\t\td = [2]float64{3.543889200, 1.637067800}\n\t)\n\n\tconst y0 = 0.7\n\tvar x, z float64\n\n\tif math.Abs(y) == 1.0 {\n\t\tx = -y * math.Log(0.0)\n\t} else if y < -y0 {\n\t\tz = math.Sqrt(-math.Log((1.0 + y) / 2.0))\n\t\tx = -(((c[3]*z+c[2])*z+c[1])*z + c[0]) / ((d[1]*z+d[0])*z + 1.0)\n\t} else {\n\t\tif y < y0 {\n\t\t\tz = y * y\n\t\t\tx = y * (((a[3]*z+a[2])*z+a[1])*z + a[0]) / ((((b[3]*z+b[3])*z+b[1])*z+b[0])*z + 1.0)\n\t\t} else {\n\t\t\tz = math.Sqrt(-math.Log((1.0 - y) / 2.0))\n\t\t\tx = (((c[3]*z+c[2])*z+c[1])*z + c[0]) / ((d[1]*z+d[0])*z + 1.0)\n\t\t}\n\t\tx -= (math.Erf(x) - y) / (2.0 / math.SqrtPi * math.Exp(-x*x))\n\t\tx -= (math.Erf(x) - y) / (2.0 / math.SqrtPi * math.Exp(-x*x))\n\t}\n\n\treturn x\n}\n"
  },
  {
    "path": "metrics/teststat/teststat.go",
    "content": "// Package teststat provides helpers for testing metrics backends.\npackage teststat\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"math/rand\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/go-kit/kit/metrics\"\n)\n\n// TestCounter puts some deltas through the counter, and then calls the value\n// func to check that the counter has the correct final value.\nfunc TestCounter(counter metrics.Counter, value func() float64) error {\n\twant := FillCounter(counter)\n\tif have := value(); want != have {\n\t\treturn fmt.Errorf(\"want %f, have %f\", want, have)\n\t}\n\n\treturn nil\n}\n\n// FillCounter puts some deltas through the counter and returns the total value.\nfunc FillCounter(counter metrics.Counter) float64 {\n\ta := rand.Perm(100)\n\tn := rand.Intn(len(a))\n\n\tvar want float64\n\tfor i := 0; i < n; i++ {\n\t\tf := float64(a[i])\n\t\tcounter.Add(f)\n\t\twant += f\n\t}\n\treturn want\n}\n\n// TestGauge puts some values through the gauge, and then calls the value func\n// to check that the gauge has the correct final value.\nfunc TestGauge(gauge metrics.Gauge, value func() []float64) error {\n\ta := rand.Perm(100)\n\tn := rand.Intn(len(a))\n\n\tvar want []float64\n\tfor i := 0; i < n; i++ {\n\t\tf := float64(a[i])\n\t\tgauge.Set(f)\n\t\twant = append(want, f)\n\t}\n\n\tfor i := 0; i < n; i++ {\n\t\tf := float64(a[i])\n\t\tgauge.Add(f)\n\t\twant = append(want, want[len(want)-1]+f)\n\t}\n\n\thave := value()\n\n\tswitch len(have) {\n\tcase 0:\n\t\treturn fmt.Errorf(\"got 0 values\")\n\tcase 1: // provider doesn't support multi value\n\t\tif have[0] != want[len(want)-1] {\n\t\t\treturn fmt.Errorf(\"want %f, have %f\", want, have)\n\t\t}\n\tdefault: // provider support multi value gauges\n\t\tsort.Float64s(want)\n\t\tsort.Float64s(have)\n\t\tif !reflect.DeepEqual(want, have) {\n\t\t\treturn fmt.Errorf(\"want %f, have %f\", want, have)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// TestHistogram puts some observations through the histogram, and then calls\n// the quantiles func to checks that the histogram has computed the correct\n// quantiles within some tolerance\nfunc TestHistogram(histogram metrics.Histogram, quantiles func() (p50, p90, p95, p99 float64), tolerance float64) error {\n\tPopulateNormalHistogram(histogram, rand.Int())\n\n\twant50, want90, want95, want99 := normalQuantiles()\n\thave50, have90, have95, have99 := quantiles()\n\n\tvar errs []string\n\tif want, have := want50, have50; !cmp(want, have, tolerance) {\n\t\terrs = append(errs, fmt.Sprintf(\"p50: want %f, have %f\", want, have))\n\t}\n\tif want, have := want90, have90; !cmp(want, have, tolerance) {\n\t\terrs = append(errs, fmt.Sprintf(\"p90: want %f, have %f\", want, have))\n\t}\n\tif want, have := want95, have95; !cmp(want, have, tolerance) {\n\t\terrs = append(errs, fmt.Sprintf(\"p95: want %f, have %f\", want, have))\n\t}\n\tif want, have := want99, have99; !cmp(want, have, tolerance) {\n\t\terrs = append(errs, fmt.Sprintf(\"p99: want %f, have %f\", want, have))\n\t}\n\tif len(errs) > 0 {\n\t\treturn errors.New(strings.Join(errs, \"; \"))\n\t}\n\n\treturn nil\n}\n\nvar (\n\t// Count is the number of observations.\n\tCount = 12345\n\n\t// Mean is the center of the normal distribution of observations.\n\tMean = 500\n\n\t// Stdev of the normal distribution of observations.\n\tStdev = 25\n)\n\n// ExpectedObservationsLessThan returns the number of observations that should\n// have a value less than or equal to the given value, given a normal\n// distribution of observations described by Count, Mean, and Stdev.\nfunc ExpectedObservationsLessThan(bucket int64) int64 {\n\t// https://code.google.com/p/gostat/source/browse/stat/normal.go\n\tcdf := ((1.0 / 2.0) * (1 + math.Erf((float64(bucket)-float64(Mean))/(float64(Stdev)*math.Sqrt2))))\n\treturn int64(cdf * float64(Count))\n}\n\nfunc cmp(want, have, tol float64) bool {\n\tif (math.Abs(want-have) / want) > tol {\n\t\treturn false\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "metrics/timer.go",
    "content": "package metrics\n\nimport \"time\"\n\n// Timer acts as a stopwatch, sending observations to a wrapped histogram.\n// It's a bit of helpful syntax sugar for h.Observe(time.Since(x)).\ntype Timer struct {\n\th Histogram\n\tt time.Time\n\tu time.Duration\n}\n\n// NewTimer wraps the given histogram and records the current time.\nfunc NewTimer(h Histogram) *Timer {\n\treturn &Timer{\n\t\th: h,\n\t\tt: time.Now(),\n\t\tu: time.Second,\n\t}\n}\n\n// ObserveDuration captures the number of seconds since the timer was\n// constructed, and forwards that observation to the histogram.\nfunc (t *Timer) ObserveDuration() {\n\td := float64(time.Since(t.t).Nanoseconds()) / float64(t.u)\n\tif d < 0 {\n\t\td = 0\n\t}\n\tt.h.Observe(d)\n}\n\n// Unit sets the unit of the float64 emitted by the timer.\n// By default, the timer emits seconds.\nfunc (t *Timer) Unit(u time.Duration) {\n\tt.u = u\n}\n"
  },
  {
    "path": "metrics/timer_test.go",
    "content": "package metrics_test\n\nimport (\n\t\"math\"\n\t\"testing\"\n\n\t\"time\"\n\n\t\"github.com/go-kit/kit/metrics\"\n\t\"github.com/go-kit/kit/metrics/generic\"\n)\n\nfunc TestTimerFast(t *testing.T) {\n\th := generic.NewSimpleHistogram()\n\tmetrics.NewTimer(h).ObserveDuration()\n\n\ttolerance := 0.050\n\tif want, have := 0.000, h.ApproximateMovingAverage(); math.Abs(want-have) > tolerance {\n\t\tt.Errorf(\"want %.3f, have %.3f\", want, have)\n\t}\n}\n\nfunc TestTimerSlow(t *testing.T) {\n\th := generic.NewSimpleHistogram()\n\ttimer := metrics.NewTimer(h)\n\ttime.Sleep(250 * time.Millisecond)\n\ttimer.ObserveDuration()\n\n\ttolerance := 0.050\n\tif want, have := 0.250, h.ApproximateMovingAverage(); math.Abs(want-have) > tolerance {\n\t\tt.Errorf(\"want %.3f, have %.3f\", want, have)\n\t}\n}\n\nfunc TestTimerUnit(t *testing.T) {\n\tfor _, tc := range []struct {\n\t\tname      string\n\t\tunit      time.Duration\n\t\ttolerance float64\n\t\twant      float64\n\t}{\n\t\t{\"Seconds\", time.Second, 0.010, 0.100},\n\t\t{\"Milliseconds\", time.Millisecond, 10, 100},\n\t\t{\"Nanoseconds\", time.Nanosecond, 10000000, 100000000},\n\t} {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\th := generic.NewSimpleHistogram()\n\t\t\ttimer := metrics.NewTimer(h)\n\t\t\ttime.Sleep(100 * time.Millisecond)\n\t\t\ttimer.Unit(tc.unit)\n\t\t\ttimer.ObserveDuration()\n\n\t\t\tif want, have := tc.want, h.ApproximateMovingAverage(); math.Abs(want-have) > tc.tolerance {\n\t\t\t\tt.Errorf(\"want %.3f, have %.3f\", want, have)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "ratelimit/token_bucket.go",
    "content": "package ratelimit\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n)\n\n// ErrLimited is returned in the request path when the rate limiter is\n// triggered and the request is rejected.\nvar ErrLimited = errors.New(\"rate limit exceeded\")\n\n// Allower dictates whether or not a request is acceptable to run.\n// The Limiter from \"golang.org/x/time/rate\" already implements this interface,\n// one is able to use that in NewErroringLimiter without any modifications.\ntype Allower interface {\n\tAllow() bool\n}\n\n// NewErroringLimiter returns an endpoint.Middleware that acts as a rate\n// limiter. Requests that would exceed the\n// maximum request rate are simply rejected with an error.\nfunc NewErroringLimiter(limit Allower) endpoint.Middleware {\n\treturn func(next endpoint.Endpoint) endpoint.Endpoint {\n\t\treturn func(ctx context.Context, request interface{}) (interface{}, error) {\n\t\t\tif !limit.Allow() {\n\t\t\t\treturn nil, ErrLimited\n\t\t\t}\n\t\t\treturn next(ctx, request)\n\t\t}\n\t}\n}\n\n// Waiter dictates how long a request must be delayed.\n// The Limiter from \"golang.org/x/time/rate\" already implements this interface,\n// one is able to use that in NewDelayingLimiter without any modifications.\ntype Waiter interface {\n\tWait(ctx context.Context) error\n}\n\n// NewDelayingLimiter returns an endpoint.Middleware that acts as a\n// request throttler. Requests that would\n// exceed the maximum request rate are delayed via the Waiter function\nfunc NewDelayingLimiter(limit Waiter) endpoint.Middleware {\n\treturn func(next endpoint.Endpoint) endpoint.Endpoint {\n\t\treturn func(ctx context.Context, request interface{}) (interface{}, error) {\n\t\t\tif err := limit.Wait(ctx); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn next(ctx, request)\n\t\t}\n\t}\n}\n\n// AllowerFunc is an adapter that lets a function operate as if\n// it implements Allower\ntype AllowerFunc func() bool\n\n// Allow makes the adapter implement Allower\nfunc (f AllowerFunc) Allow() bool {\n\treturn f()\n}\n\n// WaiterFunc is an adapter that lets a function operate as if\n// it implements Waiter\ntype WaiterFunc func(ctx context.Context) error\n\n// Wait makes the adapter implement Waiter\nfunc (f WaiterFunc) Wait(ctx context.Context) error {\n\treturn f(ctx)\n}\n"
  },
  {
    "path": "ratelimit/token_bucket_test.go",
    "content": "package ratelimit_test\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"golang.org/x/time/rate\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/ratelimit\"\n)\n\nvar nopEndpoint = func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil }\n\nfunc TestXRateErroring(t *testing.T) {\n\tlimit := rate.NewLimiter(rate.Every(time.Minute), 1)\n\ttestSuccessThenFailure(\n\t\tt,\n\t\tratelimit.NewErroringLimiter(limit)(nopEndpoint),\n\t\tratelimit.ErrLimited.Error())\n}\n\nfunc TestXRateDelaying(t *testing.T) {\n\tlimit := rate.NewLimiter(rate.Every(time.Minute), 1)\n\ttestSuccessThenFailure(\n\t\tt,\n\t\tratelimit.NewDelayingLimiter(limit)(nopEndpoint),\n\t\t\"exceed context deadline\")\n}\n\nfunc testSuccessThenFailure(t *testing.T, e endpoint.Endpoint, failContains string) {\n\tctx, cxl := context.WithTimeout(context.Background(), 500*time.Millisecond)\n\tdefer cxl()\n\n\t// First request should succeed.\n\tif _, err := e(ctx, struct{}{}); err != nil {\n\t\tt.Errorf(\"unexpected: %v\\n\", err)\n\t}\n\n\t// Next request should fail.\n\tif _, err := e(ctx, struct{}{}); !strings.Contains(err.Error(), failContains) {\n\t\tt.Errorf(\"expected `%s`: %v\\n\", failContains, err)\n\t}\n}\n"
  },
  {
    "path": "sd/benchmark_test.go",
    "content": "package sd\n\nimport (\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/log\"\n)\n\nfunc BenchmarkEndpoints(b *testing.B) {\n\tvar (\n\t\tca      = make(closer)\n\t\tcb      = make(closer)\n\t\tcmap    = map[string]io.Closer{\"a\": ca, \"b\": cb}\n\t\tfactory = func(instance string) (endpoint.Endpoint, io.Closer, error) { return endpoint.Nop, cmap[instance], nil }\n\t\tc       = newEndpointCache(factory, log.NewNopLogger(), endpointerOptions{})\n\t)\n\n\tb.ReportAllocs()\n\n\tc.Update(Event{Instances: []string{\"a\", \"b\"}})\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tc.Endpoints()\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "sd/consul/client.go",
    "content": "package consul\n\nimport (\n\tconsul \"github.com/hashicorp/consul/api\"\n)\n\n// Client is a wrapper around the Consul API.\ntype Client interface {\n\t// Register a service with the local agent.\n\tRegister(r *consul.AgentServiceRegistration) error\n\n\t// Deregister a service with the local agent.\n\tDeregister(r *consul.AgentServiceRegistration) error\n\n\t// Service\n\tService(service, tag string, passingOnly bool, queryOpts *consul.QueryOptions) ([]*consul.ServiceEntry, *consul.QueryMeta, error)\n}\n\ntype client struct {\n\tconsul *consul.Client\n}\n\n// NewClient returns an implementation of the Client interface, wrapping a\n// concrete Consul client.\nfunc NewClient(c *consul.Client) Client {\n\treturn &client{consul: c}\n}\n\nfunc (c *client) Register(r *consul.AgentServiceRegistration) error {\n\treturn c.consul.Agent().ServiceRegister(r)\n}\n\nfunc (c *client) Deregister(r *consul.AgentServiceRegistration) error {\n\treturn c.consul.Agent().ServiceDeregister(r.ID)\n}\n\nfunc (c *client) Service(service, tag string, passingOnly bool, queryOpts *consul.QueryOptions) ([]*consul.ServiceEntry, *consul.QueryMeta, error) {\n\treturn c.consul.Health().Service(service, tag, passingOnly, queryOpts)\n}\n"
  },
  {
    "path": "sd/consul/client_test.go",
    "content": "package consul\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"io\"\n\t\"reflect\"\n\t\"testing\"\n\n\tstdconsul \"github.com/hashicorp/consul/api\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n)\n\nfunc TestClientRegistration(t *testing.T) {\n\tc := newTestClient(nil)\n\n\tservices, _, err := c.Service(testRegistration.Name, \"\", true, &stdconsul.QueryOptions{})\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif want, have := 0, len(services); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\tif err := c.Register(testRegistration); err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif err := c.Register(testRegistration); err == nil {\n\t\tt.Errorf(\"want error, have %v\", err)\n\t}\n\n\tservices, _, err = c.Service(testRegistration.Name, \"\", true, &stdconsul.QueryOptions{})\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif want, have := 1, len(services); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\tif err := c.Deregister(testRegistration); err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif err := c.Deregister(testRegistration); err == nil {\n\t\tt.Errorf(\"want error, have %v\", err)\n\t}\n\n\tservices, _, err = c.Service(testRegistration.Name, \"\", true, &stdconsul.QueryOptions{})\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif want, have := 0, len(services); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\ntype testClient struct {\n\tentries []*stdconsul.ServiceEntry\n}\n\nfunc newTestClient(entries []*stdconsul.ServiceEntry) *testClient {\n\treturn &testClient{\n\t\tentries: entries,\n\t}\n}\n\nvar _ Client = &testClient{}\n\nfunc (c *testClient) Service(service, tag string, _ bool, opts *stdconsul.QueryOptions) ([]*stdconsul.ServiceEntry, *stdconsul.QueryMeta, error) {\n\tvar results []*stdconsul.ServiceEntry\n\n\tfor _, entry := range c.entries {\n\t\tif entry.Service.Service != service {\n\t\t\tcontinue\n\t\t}\n\t\tif tag != \"\" {\n\t\t\ttagMap := map[string]struct{}{}\n\n\t\t\tfor _, t := range entry.Service.Tags {\n\t\t\t\ttagMap[t] = struct{}{}\n\t\t\t}\n\n\t\t\tif _, ok := tagMap[tag]; !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tresults = append(results, entry)\n\t}\n\n\treturn results, &stdconsul.QueryMeta{LastIndex: opts.WaitIndex}, nil\n}\n\nfunc (c *testClient) Register(r *stdconsul.AgentServiceRegistration) error {\n\ttoAdd := registration2entry(r)\n\n\tfor _, entry := range c.entries {\n\t\tif reflect.DeepEqual(*entry, *toAdd) {\n\t\t\treturn errors.New(\"duplicate\")\n\t\t}\n\t}\n\n\tc.entries = append(c.entries, toAdd)\n\treturn nil\n}\n\nfunc (c *testClient) Deregister(r *stdconsul.AgentServiceRegistration) error {\n\ttoDelete := registration2entry(r)\n\n\tvar newEntries []*stdconsul.ServiceEntry\n\tfor _, entry := range c.entries {\n\t\tif reflect.DeepEqual(*entry, *toDelete) {\n\t\t\tcontinue\n\t\t}\n\t\tnewEntries = append(newEntries, entry)\n\t}\n\tif len(newEntries) == len(c.entries) {\n\t\treturn errors.New(\"not found\")\n\t}\n\n\tc.entries = newEntries\n\treturn nil\n}\n\nfunc registration2entry(r *stdconsul.AgentServiceRegistration) *stdconsul.ServiceEntry {\n\treturn &stdconsul.ServiceEntry{\n\t\tNode: &stdconsul.Node{\n\t\t\tNode:    \"some-node\",\n\t\t\tAddress: r.Address,\n\t\t},\n\t\tService: &stdconsul.AgentService{\n\t\t\tID:      r.ID,\n\t\t\tService: r.Name,\n\t\t\tTags:    r.Tags,\n\t\t\tPort:    r.Port,\n\t\t\tAddress: r.Address,\n\t\t},\n\t\t// Checks ignored\n\t}\n}\n\nfunc testFactory(instance string) (endpoint.Endpoint, io.Closer, error) {\n\treturn func(context.Context, interface{}) (interface{}, error) {\n\t\treturn instance, nil\n\t}, nil, nil\n}\n\nvar testRegistration = &stdconsul.AgentServiceRegistration{\n\tID:      \"my-id\",\n\tName:    \"my-name\",\n\tTags:    []string{\"my-tag-1\", \"my-tag-2\"},\n\tPort:    12345,\n\tAddress: \"my-address\",\n}\n"
  },
  {
    "path": "sd/consul/doc.go",
    "content": "// Package consul provides Instancer and Registrar implementations for Consul.\npackage consul\n"
  },
  {
    "path": "sd/consul/instancer.go",
    "content": "package consul\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\tconsul \"github.com/hashicorp/consul/api\"\n\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/kit/sd/internal/instance\"\n\t\"github.com/go-kit/kit/util/conn\"\n\t\"github.com/go-kit/log\"\n)\n\nconst defaultIndex = 0\n\n// errStopped notifies the loop to quit. aka stopped via quitc\nvar errStopped = errors.New(\"quit and closed consul instancer\")\n\n// Instancer yields instances for a service in Consul.\ntype Instancer struct {\n\tcache       *instance.Cache\n\tclient      Client\n\tlogger      log.Logger\n\tservice     string\n\ttags        []string\n\tpassingOnly bool\n\tquitc       chan struct{}\n}\n\n// NewInstancer returns a Consul instancer that publishes instances for the\n// requested service. It only returns instances for which all of the passed tags\n// are present.\nfunc NewInstancer(client Client, logger log.Logger, service string, tags []string, passingOnly bool) *Instancer {\n\ts := &Instancer{\n\t\tcache:       instance.NewCache(),\n\t\tclient:      client,\n\t\tlogger:      log.With(logger, \"service\", service, \"tags\", fmt.Sprint(tags)),\n\t\tservice:     service,\n\t\ttags:        tags,\n\t\tpassingOnly: passingOnly,\n\t\tquitc:       make(chan struct{}),\n\t}\n\n\tinstances, index, err := s.getInstances(defaultIndex, nil)\n\tif err == nil {\n\t\ts.logger.Log(\"instances\", len(instances))\n\t} else {\n\t\ts.logger.Log(\"err\", err)\n\t}\n\n\ts.cache.Update(sd.Event{Instances: instances, Err: err})\n\tgo s.loop(index)\n\treturn s\n}\n\n// Stop terminates the instancer.\nfunc (s *Instancer) Stop() {\n\tclose(s.quitc)\n}\n\nfunc (s *Instancer) loop(lastIndex uint64) {\n\tvar (\n\t\tinstances []string\n\t\terr       error\n\t\td         time.Duration = 10 * time.Millisecond\n\t\tindex     uint64\n\t)\n\tfor {\n\t\tinstances, index, err = s.getInstances(lastIndex, s.quitc)\n\t\tswitch {\n\t\tcase errors.Is(err, errStopped):\n\t\t\treturn // stopped via quitc\n\t\tcase err != nil:\n\t\t\ts.logger.Log(\"err\", err)\n\t\t\ttime.Sleep(d)\n\t\t\td = conn.Exponential(d)\n\t\t\ts.cache.Update(sd.Event{Err: err})\n\t\tcase index == defaultIndex:\n\t\t\ts.logger.Log(\"err\", \"index is not sane\")\n\t\t\ttime.Sleep(d)\n\t\t\td = conn.Exponential(d)\n\t\tcase index < lastIndex:\n\t\t\ts.logger.Log(\"err\", \"index is less than previous; resetting to default\")\n\t\t\tlastIndex = defaultIndex\n\t\t\ttime.Sleep(d)\n\t\t\td = conn.Exponential(d)\n\t\tdefault:\n\t\t\tlastIndex = index\n\t\t\ts.cache.Update(sd.Event{Instances: instances})\n\t\t\td = 10 * time.Millisecond\n\t\t}\n\t}\n}\n\nfunc (s *Instancer) getInstances(lastIndex uint64, interruptc chan struct{}) ([]string, uint64, error) {\n\ttag := \"\"\n\tif len(s.tags) > 0 {\n\t\ttag = s.tags[0]\n\t}\n\n\t// Consul doesn't support more than one tag in its service query method.\n\t// https://github.com/hashicorp/consul/issues/294\n\t// Hashi suggest prepared queries, but they don't support blocking.\n\t// https://www.consul.io/docs/agent/http/query.html#execute\n\t// If we want blocking for efficiency, we must filter tags manually.\n\n\ttype response struct {\n\t\tinstances []string\n\t\tindex     uint64\n\t}\n\n\tvar (\n\t\terrc = make(chan error, 1)\n\t\tresc = make(chan response, 1)\n\t)\n\n\tgo func() {\n\t\tentries, meta, err := s.client.Service(s.service, tag, s.passingOnly, &consul.QueryOptions{\n\t\t\tWaitIndex: lastIndex,\n\t\t})\n\t\tif err != nil {\n\t\t\terrc <- err\n\t\t\treturn\n\t\t}\n\t\tif len(s.tags) > 1 {\n\t\t\tentries = filterEntries(entries, s.tags[1:]...)\n\t\t}\n\t\tresc <- response{\n\t\t\tinstances: makeInstances(entries),\n\t\t\tindex:     meta.LastIndex,\n\t\t}\n\t}()\n\n\tselect {\n\tcase err := <-errc:\n\t\treturn nil, 0, err\n\tcase res := <-resc:\n\t\treturn res.instances, res.index, nil\n\tcase <-interruptc:\n\t\treturn nil, 0, errStopped\n\t}\n}\n\n// Register implements Instancer.\nfunc (s *Instancer) Register(ch chan<- sd.Event) {\n\ts.cache.Register(ch)\n}\n\n// Deregister implements Instancer.\nfunc (s *Instancer) Deregister(ch chan<- sd.Event) {\n\ts.cache.Deregister(ch)\n}\n\nfunc filterEntries(entries []*consul.ServiceEntry, tags ...string) []*consul.ServiceEntry {\n\tvar es []*consul.ServiceEntry\n\nENTRIES:\n\tfor _, entry := range entries {\n\t\tts := make(map[string]struct{}, len(entry.Service.Tags))\n\t\tfor _, tag := range entry.Service.Tags {\n\t\t\tts[tag] = struct{}{}\n\t\t}\n\n\t\tfor _, tag := range tags {\n\t\t\tif _, ok := ts[tag]; !ok {\n\t\t\t\tcontinue ENTRIES\n\t\t\t}\n\t\t}\n\t\tes = append(es, entry)\n\t}\n\n\treturn es\n}\n\nfunc makeInstances(entries []*consul.ServiceEntry) []string {\n\tinstances := make([]string, len(entries))\n\tfor i, entry := range entries {\n\t\taddr := entry.Node.Address\n\t\tif entry.Service.Address != \"\" {\n\t\t\taddr = entry.Service.Address\n\t\t}\n\t\tinstances[i] = fmt.Sprintf(\"%s:%d\", addr, entry.Service.Port)\n\t}\n\treturn instances\n}\n"
  },
  {
    "path": "sd/consul/instancer_test.go",
    "content": "package consul\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"testing\"\n\t\"time\"\n\n\tconsul \"github.com/hashicorp/consul/api\"\n\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/log\"\n)\n\nvar _ sd.Instancer = (*Instancer)(nil) // API check\n\nvar consulState = []*consul.ServiceEntry{\n\t{\n\t\tNode: &consul.Node{\n\t\t\tAddress: \"10.0.0.0\",\n\t\t\tNode:    \"app00.local\",\n\t\t},\n\t\tService: &consul.AgentService{\n\t\t\tID:      \"search-api-0\",\n\t\t\tPort:    8000,\n\t\t\tService: \"search\",\n\t\t\tTags: []string{\n\t\t\t\t\"api\",\n\t\t\t\t\"v1\",\n\t\t\t},\n\t\t},\n\t},\n\t{\n\t\tNode: &consul.Node{\n\t\t\tAddress: \"10.0.0.1\",\n\t\t\tNode:    \"app01.local\",\n\t\t},\n\t\tService: &consul.AgentService{\n\t\t\tID:      \"search-api-1\",\n\t\t\tPort:    8001,\n\t\t\tService: \"search\",\n\t\t\tTags: []string{\n\t\t\t\t\"api\",\n\t\t\t\t\"v2\",\n\t\t\t},\n\t\t},\n\t},\n\t{\n\t\tNode: &consul.Node{\n\t\t\tAddress: \"10.0.0.1\",\n\t\t\tNode:    \"app01.local\",\n\t\t},\n\t\tService: &consul.AgentService{\n\t\t\tAddress: \"10.0.0.10\",\n\t\t\tID:      \"search-db-0\",\n\t\t\tPort:    9000,\n\t\t\tService: \"search\",\n\t\t\tTags: []string{\n\t\t\t\t\"db\",\n\t\t\t},\n\t\t},\n\t},\n}\n\nfunc TestInstancer(t *testing.T) {\n\tvar (\n\t\tlogger = log.NewNopLogger()\n\t\tclient = newTestClient(consulState)\n\t)\n\n\ts := NewInstancer(client, logger, \"search\", []string{\"api\"}, true)\n\tdefer s.Stop()\n\n\tstate := s.cache.State()\n\tif want, have := 2, len(state.Instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\nfunc TestInstancerNoService(t *testing.T) {\n\tvar (\n\t\tlogger = log.NewNopLogger()\n\t\tclient = newTestClient(consulState)\n\t)\n\n\ts := NewInstancer(client, logger, \"feed\", []string{}, true)\n\tdefer s.Stop()\n\n\tstate := s.cache.State()\n\tif want, have := 0, len(state.Instances); want != have {\n\t\tt.Fatalf(\"want %d, have %d\", want, have)\n\t}\n}\n\nfunc TestInstancerWithTags(t *testing.T) {\n\tvar (\n\t\tlogger = log.NewNopLogger()\n\t\tclient = newTestClient(consulState)\n\t)\n\n\ts := NewInstancer(client, logger, \"search\", []string{\"api\", \"v2\"}, true)\n\tdefer s.Stop()\n\n\tstate := s.cache.State()\n\tif want, have := 1, len(state.Instances); want != have {\n\t\tt.Fatalf(\"want %d, have %d\", want, have)\n\t}\n}\n\nfunc TestInstancerAddressOverride(t *testing.T) {\n\ts := NewInstancer(newTestClient(consulState), log.NewNopLogger(), \"search\", []string{\"db\"}, true)\n\tdefer s.Stop()\n\n\tstate := s.cache.State()\n\tif want, have := 1, len(state.Instances); want != have {\n\t\tt.Fatalf(\"want %d, have %d\", want, have)\n\t}\n\n\tendpoint, closer, err := testFactory(state.Instances[0])\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif closer != nil {\n\t\tdefer closer.Close()\n\t}\n\n\tresponse, err := endpoint(context.Background(), struct{}{})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif want, have := \"10.0.0.10:9000\", response.(string); want != have {\n\t\tt.Errorf(\"want %q, have %q\", want, have)\n\t}\n}\n\ntype eofTestClient struct {\n\tclient *testClient\n\teofSig chan bool\n\tcalled chan struct{}\n}\n\nfunc neweofTestClient(client *testClient, sig chan bool, called chan struct{}) Client {\n\treturn &eofTestClient{client: client, eofSig: sig, called: called}\n}\n\nfunc (c *eofTestClient) Register(r *consul.AgentServiceRegistration) error {\n\treturn c.client.Register(r)\n}\n\nfunc (c *eofTestClient) Deregister(r *consul.AgentServiceRegistration) error {\n\treturn c.client.Deregister(r)\n}\n\nfunc (c *eofTestClient) Service(service, tag string, passingOnly bool, queryOpts *consul.QueryOptions) ([]*consul.ServiceEntry, *consul.QueryMeta, error) {\n\tc.called <- struct{}{}\n\tshouldEOF := <-c.eofSig\n\tif shouldEOF {\n\t\treturn nil, &consul.QueryMeta{}, io.EOF\n\t}\n\treturn c.client.Service(service, tag, passingOnly, queryOpts)\n}\n\nfunc TestInstancerWithEOF(t *testing.T) {\n\tvar (\n\t\tsig    = make(chan bool, 1)\n\t\tcalled = make(chan struct{}, 1)\n\t\tlogger = log.NewNopLogger()\n\t\tclient = neweofTestClient(newTestClient(consulState), sig, called)\n\t)\n\n\tsig <- false\n\ts := NewInstancer(client, logger, \"search\", []string{\"api\"}, true)\n\tdefer s.Stop()\n\n\tselect {\n\tcase <-called:\n\tcase <-time.Tick(time.Millisecond * 500):\n\t\tt.Error(\"failed, to receive call\")\n\t}\n\n\tstate := s.cache.State()\n\tif want, have := 2, len(state.Instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\t// some error occurred resulting in io.EOF\n\tsig <- true\n\n\t// Service Called Once\n\tselect {\n\tcase <-called:\n\tcase <-time.Tick(time.Millisecond * 500):\n\t\tt.Error(\"failed, to receive call in time\")\n\t}\n\n\tsig <- false\n\n\t// loop should continue\n\tselect {\n\tcase <-called:\n\tcase <-time.Tick(time.Millisecond * 500):\n\t\tt.Error(\"failed, to receive call in time\")\n\t}\n}\n\ntype badIndexTestClient struct {\n\tclient *testClient\n\tcalled chan struct{}\n}\n\nfunc newBadIndexTestClient(client *testClient, called chan struct{}) Client {\n\treturn &badIndexTestClient{client: client, called: called}\n}\n\nfunc (c *badIndexTestClient) Register(r *consul.AgentServiceRegistration) error {\n\treturn c.client.Register(r)\n}\n\nfunc (c *badIndexTestClient) Deregister(r *consul.AgentServiceRegistration) error {\n\treturn c.client.Deregister(r)\n}\n\nfunc (c *badIndexTestClient) Service(service, tag string, passingOnly bool, queryOpts *consul.QueryOptions) ([]*consul.ServiceEntry, *consul.QueryMeta, error) {\n\tswitch {\n\tcase queryOpts.WaitIndex == 0:\n\t\tqueryOpts.WaitIndex = 100\n\tcase queryOpts.WaitIndex == 100:\n\t\tqueryOpts.WaitIndex = 99\n\tdefault:\n\t}\n\tc.called <- struct{}{}\n\treturn c.client.Service(service, tag, passingOnly, queryOpts)\n}\n\nfunc TestInstancerWithInvalidIndex(t *testing.T) {\n\tvar (\n\t\tcalled = make(chan struct{}, 1)\n\t\tlogger = log.NewNopLogger()\n\t\tclient = newBadIndexTestClient(newTestClient(consulState), called)\n\t)\n\n\ts := NewInstancer(client, logger, \"search\", []string{\"api\"}, true)\n\tdefer s.Stop()\n\n\tselect {\n\tcase <-called:\n\tcase <-time.Tick(time.Millisecond * 500):\n\t\tt.Error(\"failed, to receive call\")\n\t}\n\n\tstate := s.cache.State()\n\tif want, have := 2, len(state.Instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\t// loop should continue\n\tselect {\n\tcase <-called:\n\tcase <-time.Tick(time.Millisecond * 500):\n\t\tt.Error(\"failed, to receive call in time\")\n\t}\n}\n\ntype indexTestClient struct {\n\tclient *testClient\n\tindex  uint64\n\terrs   chan error\n}\n\nfunc newIndexTestClient(c *testClient, errs chan error) *indexTestClient {\n\treturn &indexTestClient{\n\t\tclient: c,\n\t\tindex:  0,\n\t\terrs:   errs,\n\t}\n}\n\nfunc (i *indexTestClient) Register(r *consul.AgentServiceRegistration) error {\n\treturn i.client.Register(r)\n}\n\nfunc (i *indexTestClient) Deregister(r *consul.AgentServiceRegistration) error {\n\treturn i.client.Deregister(r)\n}\n\nfunc (i *indexTestClient) Service(service, tag string, passingOnly bool, queryOpts *consul.QueryOptions) ([]*consul.ServiceEntry, *consul.QueryMeta, error) {\n\n\t// Assumes this is the first call Service, loop hasn't begun running yet\n\tif i.index == 0 && queryOpts.WaitIndex == 0 {\n\t\ti.index = 100\n\t\tentries, meta, err := i.client.Service(service, tag, passingOnly, queryOpts)\n\t\tmeta.LastIndex = i.index\n\t\treturn entries, meta, err\n\t}\n\n\tif queryOpts.WaitIndex < i.index {\n\t\ti.errs <- fmt.Errorf(\"wait index %d is less than or equal to previous value\", queryOpts.WaitIndex)\n\t}\n\n\tentries, meta, err := i.client.Service(service, tag, passingOnly, queryOpts)\n\ti.index++\n\tmeta.LastIndex = i.index\n\treturn entries, meta, err\n}\n\nfunc TestInstancerLoopIndex(t *testing.T) {\n\n\tvar (\n\t\terrs   = make(chan error, 1)\n\t\tlogger = log.NewNopLogger()\n\t\tclient = newIndexTestClient(newTestClient(consulState), errs)\n\t)\n\n\tgo func() {\n\t\tfor err := range errs {\n\t\t\tt.Error(err)\n\t\t\tt.FailNow()\n\t\t}\n\t}()\n\n\tinstancer := NewInstancer(client, logger, \"search\", []string{\"api\"}, true)\n\tdefer instancer.Stop()\n\n\ttime.Sleep(2 * time.Second)\n}\n"
  },
  {
    "path": "sd/consul/integration_test.go",
    "content": "//go:build integration\n// +build integration\n\npackage consul\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/log\"\n\tstdconsul \"github.com/hashicorp/consul/api\"\n)\n\nfunc TestIntegration(t *testing.T) {\n\tconsulAddr := os.Getenv(\"CONSUL_ADDR\")\n\tif consulAddr == \"\" {\n\t\tt.Skip(\"CONSUL_ADDR not set; skipping integration test\")\n\t}\n\tstdClient, err := stdconsul.NewClient(&stdconsul.Config{\n\t\tAddress: consulAddr,\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tclient := NewClient(stdClient)\n\tlogger := log.NewLogfmtLogger(os.Stderr)\n\n\t// Produce a fake service registration.\n\tr := &stdconsul.AgentServiceRegistration{\n\t\tID:                \"my-service-ID\",\n\t\tName:              \"my-service-name\",\n\t\tTags:              []string{\"alpha\", \"beta\"},\n\t\tPort:              12345,\n\t\tAddress:           \"my-address\",\n\t\tEnableTagOverride: false,\n\t\t// skipping check(s)\n\t}\n\n\t// Build an Instancer on r.Name + r.Tags.\n\tfactory := func(instance string) (endpoint.Endpoint, io.Closer, error) {\n\t\tt.Logf(\"factory invoked for %q\", instance)\n\t\treturn endpoint.Nop, nil, nil\n\t}\n\tinstancer := NewInstancer(\n\t\tclient,\n\t\tlog.With(logger, \"component\", \"instancer\"),\n\t\tr.Name,\n\t\tr.Tags,\n\t\ttrue,\n\t)\n\tendpointer := sd.NewEndpointer(\n\t\tinstancer,\n\t\tfactory,\n\t\tlog.With(logger, \"component\", \"endpointer\"),\n\t)\n\n\ttime.Sleep(time.Second)\n\n\t// Before we publish, we should have no endpoints.\n\tendpoints, err := endpointer.Endpoints()\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif want, have := 0, len(endpoints); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\t// Build a registrar for r.\n\tregistrar := NewRegistrar(client, r, log.With(logger, \"component\", \"registrar\"))\n\tregistrar.Register()\n\tdefer registrar.Deregister()\n\n\ttime.Sleep(time.Second)\n\n\t// Now we should have one active endpoints.\n\tendpoints, err = endpointer.Endpoints()\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif want, have := 1, len(endpoints); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n"
  },
  {
    "path": "sd/consul/registrar.go",
    "content": "package consul\n\nimport (\n\t\"fmt\"\n\n\tstdconsul \"github.com/hashicorp/consul/api\"\n\n\t\"github.com/go-kit/log\"\n)\n\n// Registrar registers service instance liveness information to Consul.\ntype Registrar struct {\n\tclient       Client\n\tregistration *stdconsul.AgentServiceRegistration\n\tlogger       log.Logger\n}\n\n// NewRegistrar returns a Consul Registrar acting on the provided catalog\n// registration.\nfunc NewRegistrar(client Client, r *stdconsul.AgentServiceRegistration, logger log.Logger) *Registrar {\n\treturn &Registrar{\n\t\tclient:       client,\n\t\tregistration: r,\n\t\tlogger:       log.With(logger, \"service\", r.Name, \"tags\", fmt.Sprint(r.Tags), \"address\", r.Address),\n\t}\n}\n\n// Register implements sd.Registrar interface.\nfunc (p *Registrar) Register() {\n\tif err := p.client.Register(p.registration); err != nil {\n\t\tp.logger.Log(\"err\", err)\n\t} else {\n\t\tp.logger.Log(\"action\", \"register\")\n\t}\n}\n\n// Deregister implements sd.Registrar interface.\nfunc (p *Registrar) Deregister() {\n\tif err := p.client.Deregister(p.registration); err != nil {\n\t\tp.logger.Log(\"err\", err)\n\t} else {\n\t\tp.logger.Log(\"action\", \"deregister\")\n\t}\n}\n"
  },
  {
    "path": "sd/consul/registrar_test.go",
    "content": "package consul\n\nimport (\n\t\"testing\"\n\n\tstdconsul \"github.com/hashicorp/consul/api\"\n\n\t\"github.com/go-kit/log\"\n)\n\nfunc TestRegistrar(t *testing.T) {\n\tclient := newTestClient([]*stdconsul.ServiceEntry{})\n\tp := NewRegistrar(client, testRegistration, log.NewNopLogger())\n\tif want, have := 0, len(client.entries); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\tp.Register()\n\tif want, have := 1, len(client.entries); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\tp.Deregister()\n\tif want, have := 0, len(client.entries); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n"
  },
  {
    "path": "sd/dnssrv/doc.go",
    "content": "// Package dnssrv provides an Instancer implementation for DNS SRV records.\npackage dnssrv\n"
  },
  {
    "path": "sd/dnssrv/instancer.go",
    "content": "package dnssrv\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/kit/sd/internal/instance\"\n\t\"github.com/go-kit/log\"\n)\n\n// ErrPortZero is returned by the resolve machinery\n// when a DNS resolver returns an SRV record with its\n// port set to zero.\nvar ErrPortZero = errors.New(\"resolver returned SRV record with port 0\")\n\n// Instancer yields instances from the named DNS SRV record. The name is\n// resolved on a fixed schedule. Priorities and weights are ignored.\ntype Instancer struct {\n\tcache  *instance.Cache\n\tname   string\n\tlogger log.Logger\n\tquit   chan struct{}\n}\n\n// NewInstancer returns a DNS SRV instancer.\nfunc NewInstancer(\n\tname string,\n\tttl time.Duration,\n\tlogger log.Logger,\n) *Instancer {\n\treturn NewInstancerDetailed(name, time.NewTicker(ttl), net.LookupSRV, logger)\n}\n\n// NewInstancerDetailed is the same as NewInstancer, but allows users to\n// provide an explicit lookup refresh ticker instead of a TTL, and specify the\n// lookup function instead of using net.LookupSRV.\nfunc NewInstancerDetailed(\n\tname string,\n\trefresh *time.Ticker,\n\tlookup Lookup,\n\tlogger log.Logger,\n) *Instancer {\n\tp := &Instancer{\n\t\tcache:  instance.NewCache(),\n\t\tname:   name,\n\t\tlogger: logger,\n\t\tquit:   make(chan struct{}),\n\t}\n\n\tinstances, err := p.resolve(lookup)\n\tif err == nil {\n\t\tlogger.Log(\"name\", name, \"instances\", len(instances))\n\t} else {\n\t\tlogger.Log(\"name\", name, \"err\", err)\n\t}\n\tp.cache.Update(sd.Event{Instances: instances, Err: err})\n\n\tgo p.loop(refresh, lookup)\n\treturn p\n}\n\n// Stop terminates the Instancer.\nfunc (in *Instancer) Stop() {\n\tclose(in.quit)\n}\n\nfunc (in *Instancer) loop(t *time.Ticker, lookup Lookup) {\n\tdefer t.Stop()\n\tfor {\n\t\tselect {\n\t\tcase <-t.C:\n\t\t\tinstances, err := in.resolve(lookup)\n\t\t\tif err != nil {\n\t\t\t\tin.logger.Log(\"name\", in.name, \"err\", err)\n\t\t\t\tin.cache.Update(sd.Event{Err: err})\n\t\t\t\tcontinue // don't replace potentially-good with bad\n\t\t\t}\n\t\t\tin.cache.Update(sd.Event{Instances: instances})\n\n\t\tcase <-in.quit:\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (in *Instancer) resolve(lookup Lookup) ([]string, error) {\n\t_, addrs, err := lookup(\"\", \"\", in.name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tinstances := make([]string, len(addrs))\n\tfor i, addr := range addrs {\n\t\tif addr.Port == 0 {\n\t\t\treturn nil, ErrPortZero\n\t\t}\n\t\tinstances[i] = net.JoinHostPort(addr.Target, fmt.Sprint(addr.Port))\n\t}\n\treturn instances, nil\n}\n\n// Register implements Instancer.\nfunc (in *Instancer) Register(ch chan<- sd.Event) {\n\tin.cache.Register(ch)\n}\n\n// Deregister implements Instancer.\nfunc (in *Instancer) Deregister(ch chan<- sd.Event) {\n\tin.cache.Deregister(ch)\n}\n"
  },
  {
    "path": "sd/dnssrv/instancer_test.go",
    "content": "package dnssrv\n\nimport (\n\t\"net\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/log\"\n)\n\nvar _ sd.Instancer = (*Instancer)(nil) // API check\n\nfunc TestRefresh(t *testing.T) {\n\tname := \"some.service.internal\"\n\n\tticker := time.NewTicker(time.Second)\n\tticker.Stop()\n\ttickc := make(chan time.Time)\n\tticker.C = tickc\n\n\tvar lookups uint64\n\trecords := []*net.SRV{}\n\tlookup := func(service, proto, name string) (string, []*net.SRV, error) {\n\t\tt.Logf(\"lookup(%q, %q, %q)\", service, proto, name)\n\t\tatomic.AddUint64(&lookups, 1)\n\t\treturn \"cname\", records, nil\n\t}\n\n\tinstancer := NewInstancerDetailed(name, ticker, lookup, log.NewNopLogger())\n\tdefer instancer.Stop()\n\n\t// First lookup, empty\n\tstate := instancer.cache.State()\n\tif state.Err != nil {\n\t\tt.Error(state.Err)\n\t}\n\tif want, have := 0, len(state.Instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\tif want, have := uint64(1), atomic.LoadUint64(&lookups); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\t// Load some records and lookup again\n\trecords = []*net.SRV{\n\t\t{Target: \"1.0.0.1\", Port: 1001},\n\t\t{Target: \"1.0.0.2\", Port: 1002},\n\t\t{Target: \"1.0.0.3\", Port: 1003},\n\t}\n\ttickc <- time.Now()\n\n\t// There is a race condition where the instancer.State call below\n\t// invokes the cache before it is updated by the tick above.\n\t// TODO(pb): solve by running the read through the loop goroutine.\n\ttime.Sleep(100 * time.Millisecond)\n\n\tstate = instancer.cache.State()\n\tif state.Err != nil {\n\t\tt.Error(state.Err)\n\t}\n\tif want, have := 3, len(state.Instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\tif want, have := uint64(2), atomic.LoadUint64(&lookups); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\nfunc TestIssue892(t *testing.T) {\n\tticker := time.NewTicker(time.Second)\n\tticker.Stop()\n\ttickc := make(chan time.Time)\n\tticker.C = tickc\n\n\trecords := []*net.SRV{\n\t\t{Target: \"1.0.0.1\", Port: 80},\n\t\t{Target: \"1.0.0.2\", Port: 0},\n\t\t{Target: \"1.0.0.3\", Port: 80},\n\t}\n\n\tlookup := func(service, proto, name string) (string, []*net.SRV, error) {\n\t\treturn \"cname\", records, nil\n\t}\n\n\tinstancer := NewInstancerDetailed(\"name\", ticker, lookup, log.NewNopLogger())\n\tdefer instancer.Stop()\n\n\ttickc <- time.Now()\n\ttime.Sleep(100 * time.Millisecond)\n\n\tif want, have := ErrPortZero, instancer.cache.State().Err; want != have {\n\t\tt.Fatalf(\"want %v, have %v\", want, have)\n\t}\n}\n\ntype nopCloser struct{}\n\nfunc (nopCloser) Close() error { return nil }\n"
  },
  {
    "path": "sd/dnssrv/lookup.go",
    "content": "package dnssrv\n\nimport \"net\"\n\n// Lookup is a function that resolves a DNS SRV record to multiple addresses.\n// It has the same signature as net.LookupSRV.\ntype Lookup func(service, proto, name string) (cname string, addrs []*net.SRV, err error)\n"
  },
  {
    "path": "sd/doc.go",
    "content": "// Package sd provides utilities related to service discovery. That includes the\n// client-side loadbalancer pattern, where a microservice subscribes to a\n// service discovery system in order to reach remote instances; as well as the\n// registrator pattern, where a microservice registers itself in a service\n// discovery system. Implementations are provided for most common systems.\npackage sd\n"
  },
  {
    "path": "sd/endpoint_cache.go",
    "content": "package sd\n\nimport (\n\t\"io\"\n\t\"sort\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/log\"\n)\n\n// endpointCache collects the most recent set of instances from a service discovery\n// system, creates endpoints for them using a factory function, and makes\n// them available to consumers.\ntype endpointCache struct {\n\toptions            endpointerOptions\n\tmtx                sync.RWMutex\n\tfactory            Factory\n\tcache              map[string]endpointCloser\n\terr                error\n\tendpoints          []endpoint.Endpoint\n\tlogger             log.Logger\n\tinvalidateDeadline time.Time\n\ttimeNow            func() time.Time\n}\n\ntype endpointCloser struct {\n\tendpoint.Endpoint\n\tio.Closer\n}\n\n// newEndpointCache returns a new, empty endpointCache.\nfunc newEndpointCache(factory Factory, logger log.Logger, options endpointerOptions) *endpointCache {\n\treturn &endpointCache{\n\t\toptions: options,\n\t\tfactory: factory,\n\t\tcache:   map[string]endpointCloser{},\n\t\tlogger:  logger,\n\t\ttimeNow: time.Now,\n\t}\n}\n\n// Update should be invoked by clients with a complete set of current instance\n// strings whenever that set changes. The cache manufactures new endpoints via\n// the factory, closes old endpoints when they disappear, and persists existing\n// endpoints if they survive through an update.\nfunc (c *endpointCache) Update(event Event) {\n\tc.mtx.Lock()\n\tdefer c.mtx.Unlock()\n\n\t// Happy path.\n\tif event.Err == nil {\n\t\tc.updateCache(event.Instances)\n\t\tc.err = nil\n\t\treturn\n\t}\n\n\t// Sad path. Something's gone wrong in sd.\n\tc.logger.Log(\"err\", event.Err)\n\tif !c.options.invalidateOnError {\n\t\treturn // keep returning the last known endpoints on error\n\t}\n\tif c.err != nil {\n\t\treturn // already in the error state, do nothing & keep original error\n\t}\n\tc.err = event.Err\n\t// set new deadline to invalidate Endpoints unless non-error Event is received\n\tc.invalidateDeadline = c.timeNow().Add(c.options.invalidateTimeout)\n\treturn\n}\n\nfunc (c *endpointCache) updateCache(instances []string) {\n\t// Deterministic order (for later).\n\tsort.Strings(instances)\n\n\t// Produce the current set of services.\n\tcache := make(map[string]endpointCloser, len(instances))\n\tfor _, instance := range instances {\n\t\t// If it already exists, just copy it over.\n\t\tif sc, ok := c.cache[instance]; ok {\n\t\t\tcache[instance] = sc\n\t\t\tdelete(c.cache, instance)\n\t\t\tcontinue\n\t\t}\n\n\t\t// If it doesn't exist, create it.\n\t\tservice, closer, err := c.factory(instance)\n\t\tif err != nil {\n\t\t\tc.logger.Log(\"instance\", instance, \"err\", err)\n\t\t\tcontinue\n\t\t}\n\t\tcache[instance] = endpointCloser{service, closer}\n\t}\n\n\t// Close any leftover endpoints.\n\tfor _, sc := range c.cache {\n\t\tif sc.Closer != nil {\n\t\t\tsc.Closer.Close()\n\t\t}\n\t}\n\n\t// Populate the slice of endpoints.\n\tendpoints := make([]endpoint.Endpoint, 0, len(cache))\n\tfor _, instance := range instances {\n\t\t// A bad factory may mean an instance is not present.\n\t\tif _, ok := cache[instance]; !ok {\n\t\t\tcontinue\n\t\t}\n\t\tendpoints = append(endpoints, cache[instance].Endpoint)\n\t}\n\n\t// Swap and trigger GC for old copies.\n\tc.endpoints = endpoints\n\tc.cache = cache\n}\n\n// Endpoints yields the current set of (presumably identical) endpoints, ordered\n// lexicographically by the corresponding instance string.\nfunc (c *endpointCache) Endpoints() ([]endpoint.Endpoint, error) {\n\t// in the steady state we're going to have many goroutines calling Endpoints()\n\t// concurrently, so to minimize contention we use a shared R-lock.\n\tc.mtx.RLock()\n\n\tif c.err == nil || c.timeNow().Before(c.invalidateDeadline) {\n\t\tdefer c.mtx.RUnlock()\n\t\treturn c.endpoints, nil\n\t}\n\n\tc.mtx.RUnlock()\n\n\t// in case of an error, switch to an exclusive lock.\n\tc.mtx.Lock()\n\tdefer c.mtx.Unlock()\n\n\t// re-check condition due to a race between RUnlock() and Lock().\n\tif c.err == nil || c.timeNow().Before(c.invalidateDeadline) {\n\t\treturn c.endpoints, nil\n\t}\n\n\tc.updateCache(nil) // close any remaining active endpoints\n\treturn nil, c.err\n}\n"
  },
  {
    "path": "sd/endpoint_cache_test.go",
    "content": "package sd\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/log\"\n)\n\nfunc TestEndpointCache(t *testing.T) {\n\tvar (\n\t\tca    = make(closer)\n\t\tcb    = make(closer)\n\t\tc     = map[string]io.Closer{\"a\": ca, \"b\": cb}\n\t\tf     = func(instance string) (endpoint.Endpoint, io.Closer, error) { return endpoint.Nop, c[instance], nil }\n\t\tcache = newEndpointCache(f, log.NewNopLogger(), endpointerOptions{})\n\t)\n\n\t// Populate\n\tcache.Update(Event{Instances: []string{\"a\", \"b\"}})\n\tselect {\n\tcase <-ca:\n\t\tt.Errorf(\"endpoint a closed, not good\")\n\tcase <-cb:\n\t\tt.Errorf(\"endpoint b closed, not good\")\n\tcase <-time.After(time.Millisecond):\n\t\tt.Logf(\"no closures yet, good\")\n\t}\n\tassertEndpointsLen(t, cache, 2)\n\n\t// Duplicate, should be no-op\n\tcache.Update(Event{Instances: []string{\"a\", \"b\"}})\n\tselect {\n\tcase <-ca:\n\t\tt.Errorf(\"endpoint a closed, not good\")\n\tcase <-cb:\n\t\tt.Errorf(\"endpoint b closed, not good\")\n\tcase <-time.After(time.Millisecond):\n\t\tt.Logf(\"no closures yet, good\")\n\t}\n\tassertEndpointsLen(t, cache, 2)\n\n\t// Error, should continue returning old endpoints\n\tcache.Update(Event{Err: errors.New(\"sd error\")})\n\tselect {\n\tcase <-ca:\n\t\tt.Errorf(\"endpoint a closed, not good\")\n\tcase <-cb:\n\t\tt.Errorf(\"endpoint b closed, not good\")\n\tcase <-time.After(time.Millisecond):\n\t\tt.Logf(\"no closures yet, good\")\n\t}\n\tassertEndpointsLen(t, cache, 2)\n\n\t// Delete b\n\tgo cache.Update(Event{Instances: []string{\"a\"}})\n\tselect {\n\tcase <-ca:\n\t\tt.Errorf(\"endpoint a closed, not good\")\n\tcase <-cb:\n\t\tt.Logf(\"endpoint b closed, good\")\n\tcase <-time.After(time.Second):\n\t\tt.Errorf(\"didn't close the deleted instance in time\")\n\t}\n\tassertEndpointsLen(t, cache, 1)\n\n\t// Delete a\n\tgo cache.Update(Event{Instances: []string{}})\n\tselect {\n\t// case <-cb: will succeed, as it's closed\n\tcase <-ca:\n\t\tt.Logf(\"endpoint a closed, good\")\n\tcase <-time.After(time.Second):\n\t\tt.Errorf(\"didn't close the deleted instance in time\")\n\t}\n\tassertEndpointsLen(t, cache, 0)\n}\n\nfunc TestEndpointCacheErrorAndTimeout(t *testing.T) {\n\tvar (\n\t\tca      = make(closer)\n\t\tcb      = make(closer)\n\t\tc       = map[string]io.Closer{\"a\": ca, \"b\": cb}\n\t\tf       = func(instance string) (endpoint.Endpoint, io.Closer, error) { return endpoint.Nop, c[instance], nil }\n\t\ttimeOut = 100 * time.Millisecond\n\t\tcache   = newEndpointCache(f, log.NewNopLogger(), endpointerOptions{\n\t\t\tinvalidateOnError: true,\n\t\t\tinvalidateTimeout: timeOut,\n\t\t})\n\t)\n\n\ttimeNow := time.Now()\n\tcache.timeNow = func() time.Time { return timeNow }\n\n\t// Populate\n\tcache.Update(Event{Instances: []string{\"a\"}})\n\tselect {\n\tcase <-ca:\n\t\tt.Errorf(\"endpoint a closed, not good\")\n\tcase <-time.After(time.Millisecond):\n\t\tt.Logf(\"no closures yet, good\")\n\t}\n\tassertEndpointsLen(t, cache, 1)\n\n\t// Send error, keep time still.\n\tcache.Update(Event{Err: errors.New(\"sd error\")})\n\tselect {\n\tcase <-ca:\n\t\tt.Errorf(\"endpoint a closed, not good\")\n\tcase <-time.After(time.Millisecond):\n\t\tt.Logf(\"no closures yet, good\")\n\t}\n\tassertEndpointsLen(t, cache, 1)\n\n\t// Move the time, but less than the timeout\n\ttimeNow = timeNow.Add(timeOut / 2)\n\tassertEndpointsLen(t, cache, 1)\n\tselect {\n\tcase <-ca:\n\t\tt.Errorf(\"endpoint a closed, not good\")\n\tcase <-time.After(time.Millisecond):\n\t\tt.Logf(\"no closures yet, good\")\n\t}\n\n\t// Move the time past the timeout\n\ttimeNow = timeNow.Add(timeOut)\n\tassertEndpointsError(t, cache, \"sd error\")\n\tselect {\n\tcase <-ca:\n\t\tt.Logf(\"endpoint a closed, good\")\n\tcase <-time.After(time.Millisecond):\n\t\tt.Errorf(\"didn't close the deleted instance in time\")\n\t}\n\n\t// Send another error\n\tcache.Update(Event{Err: errors.New(\"another sd error\")})\n\tassertEndpointsError(t, cache, \"sd error\") // expect original error\n}\n\nfunc TestBadFactory(t *testing.T) {\n\tcache := newEndpointCache(func(string) (endpoint.Endpoint, io.Closer, error) {\n\t\treturn nil, nil, errors.New(\"bad factory\")\n\t}, log.NewNopLogger(), endpointerOptions{})\n\n\tcache.Update(Event{Instances: []string{\"foo:1234\", \"bar:5678\"}})\n\tassertEndpointsLen(t, cache, 0)\n}\n\nfunc assertEndpointsLen(t *testing.T, cache *endpointCache, l int) {\n\tendpoints, err := cache.Endpoints()\n\tif err != nil {\n\t\tt.Errorf(\"unexpected error %v\", err)\n\t\treturn\n\t}\n\tif want, have := l, len(endpoints); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\nfunc assertEndpointsError(t *testing.T, cache *endpointCache, wantErr string) {\n\tendpoints, err := cache.Endpoints()\n\tif err == nil {\n\t\tt.Errorf(\"expecting error, not good\")\n\t\treturn\n\t}\n\tif want, have := wantErr, err.Error(); want != have {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t\treturn\n\t}\n\tif want, have := 0, len(endpoints); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\ntype closer chan struct{}\n\nfunc (c closer) Close() error { close(c); return nil }\n"
  },
  {
    "path": "sd/endpointer.go",
    "content": "package sd\n\nimport (\n\t\"time\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/log\"\n)\n\n// Endpointer listens to a service discovery system and yields a set of\n// identical endpoints on demand. An error indicates a problem with connectivity\n// to the service discovery system, or within the system itself; an Endpointer\n// may yield no endpoints without error.\ntype Endpointer interface {\n\tEndpoints() ([]endpoint.Endpoint, error)\n}\n\n// FixedEndpointer yields a fixed set of endpoints.\ntype FixedEndpointer []endpoint.Endpoint\n\n// Endpoints implements Endpointer.\nfunc (s FixedEndpointer) Endpoints() ([]endpoint.Endpoint, error) { return s, nil }\n\n// NewEndpointer creates an Endpointer that subscribes to updates from Instancer src\n// and uses factory f to create Endpoints. If src notifies of an error, the Endpointer\n// keeps returning previously created Endpoints assuming they are still good, unless\n// this behavior is disabled via InvalidateOnError option.\nfunc NewEndpointer(src Instancer, f Factory, logger log.Logger, options ...EndpointerOption) *DefaultEndpointer {\n\topts := endpointerOptions{}\n\tfor _, opt := range options {\n\t\topt(&opts)\n\t}\n\tse := &DefaultEndpointer{\n\t\tcache:     newEndpointCache(f, logger, opts),\n\t\tinstancer: src,\n\t\tch:        make(chan Event),\n\t}\n\tgo se.receive()\n\tsrc.Register(se.ch)\n\treturn se\n}\n\n// EndpointerOption allows control of endpointCache behavior.\ntype EndpointerOption func(*endpointerOptions)\n\n// InvalidateOnError returns EndpointerOption that controls how the Endpointer\n// behaves when then Instancer publishes an Event containing an error.\n// Without this option the Endpointer continues returning the last known\n// endpoints. With this option, the Endpointer continues returning the last\n// known endpoints until the timeout elapses, then closes all active endpoints\n// and starts returning an error. Once the Instancer sends a new update with\n// valid resource instances, the normal operation is resumed.\nfunc InvalidateOnError(timeout time.Duration) EndpointerOption {\n\treturn func(opts *endpointerOptions) {\n\t\topts.invalidateOnError = true\n\t\topts.invalidateTimeout = timeout\n\t}\n}\n\ntype endpointerOptions struct {\n\tinvalidateOnError bool\n\tinvalidateTimeout time.Duration\n}\n\n// DefaultEndpointer implements an Endpointer interface.\n// When created with NewEndpointer function, it automatically registers\n// as a subscriber to events from the Instances and maintains a list\n// of active Endpoints.\ntype DefaultEndpointer struct {\n\tcache     *endpointCache\n\tinstancer Instancer\n\tch        chan Event\n}\n\nfunc (de *DefaultEndpointer) receive() {\n\tfor event := range de.ch {\n\t\tde.cache.Update(event)\n\t}\n}\n\n// Close deregisters DefaultEndpointer from the Instancer and stops the internal go-routine.\nfunc (de *DefaultEndpointer) Close() {\n\tde.instancer.Deregister(de.ch)\n\tclose(de.ch)\n}\n\n// Endpoints implements Endpointer.\nfunc (de *DefaultEndpointer) Endpoints() ([]endpoint.Endpoint, error) {\n\treturn de.cache.Endpoints()\n}\n"
  },
  {
    "path": "sd/endpointer_test.go",
    "content": "package sd_test\n\nimport (\n\t\"io\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/kit/sd/internal/instance\"\n\t\"github.com/go-kit/log\"\n)\n\nfunc TestDefaultEndpointer(t *testing.T) {\n\tvar (\n\t\tca = make(closer)\n\t\tcb = make(closer)\n\t\tc  = map[string]io.Closer{\"a\": ca, \"b\": cb}\n\t\tf  = func(instance string) (endpoint.Endpoint, io.Closer, error) {\n\t\t\treturn endpoint.Nop, c[instance], nil\n\t\t}\n\t\tinstancer = &mockInstancer{instance.NewCache()}\n\t)\n\t// set initial state\n\tinstancer.Update(sd.Event{Instances: []string{\"a\", \"b\"}})\n\n\tendpointer := sd.NewEndpointer(instancer, f, log.NewNopLogger(), sd.InvalidateOnError(time.Minute))\n\n\tvar (\n\t\tendpoints []endpoint.Endpoint\n\t\terr       error\n\t)\n\tif !within(time.Second, func() bool {\n\t\tendpoints, err = endpointer.Endpoints()\n\t\treturn err == nil && len(endpoints) == 2\n\t}) {\n\t\tt.Errorf(\"wanted 2 endpoints, got %d (%v)\", len(endpoints), err)\n\t}\n\n\tinstancer.Update(sd.Event{Instances: []string{}})\n\n\tselect {\n\tcase <-ca:\n\t\tt.Logf(\"endpoint a closed, good\")\n\tcase <-time.After(time.Millisecond):\n\t\tt.Errorf(\"didn't close the deleted instance in time\")\n\t}\n\n\tselect {\n\tcase <-cb:\n\t\tt.Logf(\"endpoint b closed, good\")\n\tcase <-time.After(time.Millisecond):\n\t\tt.Errorf(\"didn't close the deleted instance in time\")\n\t}\n\n\tif endpoints, err := endpointer.Endpoints(); err != nil {\n\t\tt.Errorf(\"unepected error %v\", err)\n\t} else if want, have := 0, len(endpoints); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\tendpointer.Close()\n\n\tinstancer.Update(sd.Event{Instances: []string{\"a\"}})\n\t// TODO verify that on Close the endpointer fully disconnects from the instancer.\n\t// Unfortunately, because we use instance.Cache, this test cannot be in the sd package,\n\t// and therefore does not have access to the endpointer's private members.\n}\n\ntype mockInstancer struct{ *instance.Cache }\n\ntype closer chan struct{}\n\nfunc (c closer) Close() error { close(c); return nil }\n\nfunc within(d time.Duration, f func() bool) bool {\n\tdeadline := time.Now().Add(d)\n\tfor time.Now().Before(deadline) {\n\t\tif f() {\n\t\t\treturn true\n\t\t}\n\t\ttime.Sleep(d / 10)\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "sd/etcd/client.go",
    "content": "package etcd\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"errors\"\n\t\"io/ioutil\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\tetcd \"go.etcd.io/etcd/client/v2\"\n)\n\nvar (\n\t// ErrNoKey indicates a client method needs a key but receives none.\n\tErrNoKey = errors.New(\"no key provided\")\n\n\t// ErrNoValue indicates a client method needs a value but receives none.\n\tErrNoValue = errors.New(\"no value provided\")\n)\n\n// Client is a wrapper around the etcd client.\ntype Client interface {\n\t// GetEntries queries the given prefix in etcd and returns a slice\n\t// containing the values of all keys found, recursively, underneath that\n\t// prefix.\n\tGetEntries(prefix string) ([]string, error)\n\n\t// WatchPrefix watches the given prefix in etcd for changes. When a change\n\t// is detected, it will signal on the passed channel. Clients are expected\n\t// to call GetEntries to update themselves with the latest set of complete\n\t// values. WatchPrefix will always send an initial sentinel value on the\n\t// channel after establishing the watch, to ensure that clients always\n\t// receive the latest set of values. WatchPrefix will block until the\n\t// context passed to the NewClient constructor is terminated.\n\tWatchPrefix(prefix string, ch chan struct{})\n\n\t// Register a service with etcd.\n\tRegister(s Service) error\n\n\t// Deregister a service with etcd.\n\tDeregister(s Service) error\n}\n\ntype client struct {\n\tkeysAPI etcd.KeysAPI\n\tctx     context.Context\n}\n\n// ClientOptions defines options for the etcd client. All values are optional.\n// If any duration is not specified, a default of 3 seconds will be used.\ntype ClientOptions struct {\n\tCert                    string\n\tKey                     string\n\tCACert                  string\n\tDialTimeout             time.Duration\n\tDialKeepAlive           time.Duration\n\tHeaderTimeoutPerRequest time.Duration\n}\n\n// NewClient returns Client with a connection to the named machines. It will\n// return an error if a connection to the cluster cannot be made. The parameter\n// machines needs to be a full URL with schemas. e.g. \"http://localhost:2379\"\n// will work, but \"localhost:2379\" will not.\nfunc NewClient(ctx context.Context, machines []string, options ClientOptions) (Client, error) {\n\tif options.DialTimeout == 0 {\n\t\toptions.DialTimeout = 3 * time.Second\n\t}\n\tif options.DialKeepAlive == 0 {\n\t\toptions.DialKeepAlive = 3 * time.Second\n\t}\n\tif options.HeaderTimeoutPerRequest == 0 {\n\t\toptions.HeaderTimeoutPerRequest = 3 * time.Second\n\t}\n\n\ttransport := etcd.DefaultTransport\n\tif options.Cert != \"\" && options.Key != \"\" {\n\t\ttlsCert, err := tls.LoadX509KeyPair(options.Cert, options.Key)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcaCertCt, err := ioutil.ReadFile(options.CACert)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcaCertPool := x509.NewCertPool()\n\t\tcaCertPool.AppendCertsFromPEM(caCertCt)\n\t\ttransport = &http.Transport{\n\t\t\tTLSClientConfig: &tls.Config{\n\t\t\t\tCertificates: []tls.Certificate{tlsCert},\n\t\t\t\tRootCAs:      caCertPool,\n\t\t\t},\n\t\t\tDial: func(network, address string) (net.Conn, error) {\n\t\t\t\treturn (&net.Dialer{\n\t\t\t\t\tTimeout:   options.DialTimeout,\n\t\t\t\t\tKeepAlive: options.DialKeepAlive,\n\t\t\t\t}).Dial(network, address)\n\t\t\t},\n\t\t}\n\t}\n\n\tce, err := etcd.New(etcd.Config{\n\t\tEndpoints:               machines,\n\t\tTransport:               transport,\n\t\tHeaderTimeoutPerRequest: options.HeaderTimeoutPerRequest,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &client{\n\t\tkeysAPI: etcd.NewKeysAPI(ce),\n\t\tctx:     ctx,\n\t}, nil\n}\n\n// GetEntries implements the etcd Client interface.\nfunc (c *client) GetEntries(key string) ([]string, error) {\n\tresp, err := c.keysAPI.Get(c.ctx, key, &etcd.GetOptions{Recursive: true})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Special case. Note that it's possible that len(resp.Node.Nodes) == 0 and\n\t// resp.Node.Value is also empty, in which case the key is empty and we\n\t// should not return any entries.\n\tif len(resp.Node.Nodes) == 0 && resp.Node.Value != \"\" {\n\t\treturn []string{resp.Node.Value}, nil\n\t}\n\n\tentries := make([]string, len(resp.Node.Nodes))\n\tfor i, node := range resp.Node.Nodes {\n\t\tentries[i] = node.Value\n\t}\n\treturn entries, nil\n}\n\n// WatchPrefix implements the etcd Client interface.\nfunc (c *client) WatchPrefix(prefix string, ch chan struct{}) {\n\twatch := c.keysAPI.Watcher(prefix, &etcd.WatcherOptions{AfterIndex: 0, Recursive: true})\n\tch <- struct{}{} // make sure caller invokes GetEntries\n\tfor {\n\t\tif _, err := watch.Next(c.ctx); err != nil {\n\t\t\treturn\n\t\t}\n\t\tch <- struct{}{}\n\t}\n}\n\nfunc (c *client) Register(s Service) error {\n\tif s.Key == \"\" {\n\t\treturn ErrNoKey\n\t}\n\tif s.Value == \"\" {\n\t\treturn ErrNoValue\n\t}\n\tvar err error\n\tif s.TTL != nil {\n\t\t_, err = c.keysAPI.Set(c.ctx, s.Key, s.Value, &etcd.SetOptions{\n\t\t\tPrevExist: etcd.PrevIgnore,\n\t\t\tTTL:       s.TTL.ttl,\n\t\t})\n\t} else {\n\t\t_, err = c.keysAPI.Create(c.ctx, s.Key, s.Value)\n\t}\n\treturn err\n}\n\nfunc (c *client) Deregister(s Service) error {\n\tif s.Key == \"\" {\n\t\treturn ErrNoKey\n\t}\n\t_, err := c.keysAPI.Delete(c.ctx, s.Key, s.DeleteOptions)\n\treturn err\n}\n"
  },
  {
    "path": "sd/etcd/client_test.go",
    "content": "package etcd\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\tetcd \"go.etcd.io/etcd/client/v2\"\n)\n\nfunc TestNewClient(t *testing.T) {\n\tclient, err := NewClient(\n\t\tcontext.Background(),\n\t\t[]string{\"http://irrelevant:12345\"},\n\t\tClientOptions{\n\t\t\tDialTimeout:             2 * time.Second,\n\t\t\tDialKeepAlive:           2 * time.Second,\n\t\t\tHeaderTimeoutPerRequest: 2 * time.Second,\n\t\t},\n\t)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error creating client: %v\", err)\n\t}\n\tif client == nil {\n\t\tt.Fatal(\"expected new Client, got nil\")\n\t}\n}\n\n// NewClient should fail when providing invalid or missing endpoints.\nfunc TestOptions(t *testing.T) {\n\ta, err := NewClient(\n\t\tcontext.Background(),\n\t\t[]string{},\n\t\tClientOptions{\n\t\t\tCert:                    \"\",\n\t\t\tKey:                     \"\",\n\t\t\tCACert:                  \"\",\n\t\t\tDialTimeout:             2 * time.Second,\n\t\t\tDialKeepAlive:           2 * time.Second,\n\t\t\tHeaderTimeoutPerRequest: 2 * time.Second,\n\t\t},\n\t)\n\tif err == nil {\n\t\tt.Errorf(\"expected error: %v\", err)\n\t}\n\tif a != nil {\n\t\tt.Fatalf(\"expected client to be nil on failure\")\n\t}\n\n\t_, err = NewClient(\n\t\tcontext.Background(),\n\t\t[]string{\"http://irrelevant:12345\"},\n\t\tClientOptions{\n\t\t\tCert:                    \"blank.crt\",\n\t\t\tKey:                     \"blank.key\",\n\t\t\tCACert:                  \"blank.CACert\",\n\t\t\tDialTimeout:             2 * time.Second,\n\t\t\tDialKeepAlive:           2 * time.Second,\n\t\t\tHeaderTimeoutPerRequest: 2 * time.Second,\n\t\t},\n\t)\n\tif err == nil {\n\t\tt.Errorf(\"expected error: %v\", err)\n\t}\n}\n\n// Mocks of the underlying etcd.KeysAPI interface that is called by the methods we want to test\n\n// fakeKeysAPI implements etcd.KeysAPI, event and err are channels used to emulate\n// an etcd event or error, getres will be returned when etcd.KeysAPI.Get is called.\ntype fakeKeysAPI struct {\n\tevent  chan bool\n\terr    chan bool\n\tgetres *getResult\n}\n\ntype getResult struct {\n\tresp *etcd.Response\n\terr  error\n}\n\n// Get return the content of getres or nil, nil\nfunc (fka *fakeKeysAPI) Get(ctx context.Context, key string, opts *etcd.GetOptions) (*etcd.Response, error) {\n\tif fka.getres == nil {\n\t\treturn nil, nil\n\t}\n\treturn fka.getres.resp, fka.getres.err\n}\n\n// Set is not used in the tests\nfunc (fka *fakeKeysAPI) Set(ctx context.Context, key, value string, opts *etcd.SetOptions) (*etcd.Response, error) {\n\treturn nil, nil\n}\n\n// Delete is not used in the tests\nfunc (fka *fakeKeysAPI) Delete(ctx context.Context, key string, opts *etcd.DeleteOptions) (*etcd.Response, error) {\n\treturn nil, nil\n}\n\n// Create is not used in the tests\nfunc (fka *fakeKeysAPI) Create(ctx context.Context, key, value string) (*etcd.Response, error) {\n\treturn nil, nil\n}\n\n// CreateInOrder is not used in the tests\nfunc (fka *fakeKeysAPI) CreateInOrder(ctx context.Context, dir, value string, opts *etcd.CreateInOrderOptions) (*etcd.Response, error) {\n\treturn nil, nil\n}\n\n// Update is not used in the tests\nfunc (fka *fakeKeysAPI) Update(ctx context.Context, key, value string) (*etcd.Response, error) {\n\treturn nil, nil\n}\n\n// Watcher return a fakeWatcher that will forward event and error received on the channels\nfunc (fka *fakeKeysAPI) Watcher(key string, opts *etcd.WatcherOptions) etcd.Watcher {\n\treturn &fakeWatcher{fka.event, fka.err}\n}\n\n// fakeWatcher implements etcd.Watcher\ntype fakeWatcher struct {\n\tevent chan bool\n\terr   chan bool\n}\n\n// Next blocks until an etcd event or error is emulated.\n// When an event occurs it just return nil response and error.\n// When an error occur it return a non nil error.\nfunc (fw *fakeWatcher) Next(context.Context) (*etcd.Response, error) {\n\tselect {\n\tcase <-fw.event:\n\t\treturn nil, nil\n\tcase <-fw.err:\n\t\treturn nil, errors.New(\"error from underlying etcd watcher\")\n\n\t}\n}\n\n// newFakeClient return a new etcd.Client built on top of the mocked interfaces\nfunc newFakeClient(event, err chan bool, getres *getResult) Client {\n\treturn &client{\n\t\tkeysAPI: &fakeKeysAPI{event, err, getres},\n\t\tctx:     context.Background(),\n\t}\n}\n\n// Register should fail when the provided service has an empty key or value\nfunc TestRegisterClient(t *testing.T) {\n\tclient := newFakeClient(nil, nil, nil)\n\n\terr := client.Register(Service{Key: \"\", Value: \"value\", DeleteOptions: nil})\n\tif want, have := ErrNoKey, err; want != have {\n\t\tt.Fatalf(\"want %v, have %v\", want, have)\n\t}\n\n\terr = client.Register(Service{Key: \"key\", Value: \"\", DeleteOptions: nil})\n\tif want, have := ErrNoValue, err; want != have {\n\t\tt.Fatalf(\"want %v, have %v\", want, have)\n\t}\n\n\terr = client.Register(Service{Key: \"key\", Value: \"value\", DeleteOptions: nil})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\n// Deregister should fail if the input service has an empty key\nfunc TestDeregisterClient(t *testing.T) {\n\tclient := newFakeClient(nil, nil, nil)\n\n\terr := client.Deregister(Service{Key: \"\", Value: \"value\", DeleteOptions: nil})\n\tif want, have := ErrNoKey, err; want != have {\n\t\tt.Fatalf(\"want %v, have %v\", want, have)\n\t}\n\n\terr = client.Deregister(Service{Key: \"key\", Value: \"\", DeleteOptions: nil})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\n// WatchPrefix notify the caller by writing on the channel if an etcd event occurs\n// or return in case of an underlying error\nfunc TestWatchPrefix(t *testing.T) {\n\terr := make(chan bool)\n\tevent := make(chan bool)\n\twatchPrefixReturned := make(chan bool, 1)\n\tclient := newFakeClient(event, err, nil)\n\n\tch := make(chan struct{})\n\tgo func() {\n\t\tclient.WatchPrefix(\"prefix\", ch) // block until an etcd event or error occurs\n\t\twatchPrefixReturned <- true\n\t}()\n\n\t// WatchPrefix force the caller to read once from the channel before actually\n\t// sending notification, emulate that first read.\n\t<-ch\n\n\t// Emulate an etcd event\n\tevent <- true\n\tif want, have := struct{}{}, <-ch; want != have {\n\t\tt.Fatalf(\"want %v, have %v\", want, have)\n\t}\n\n\t// Emulate an error, WatchPrefix should return\n\terr <- true\n\tselect {\n\tcase <-watchPrefixReturned:\n\t\tbreak\n\tcase <-time.After(1 * time.Second):\n\t\tt.Fatal(\"WatchPrefix not returning on errors\")\n\t}\n}\n\nvar errKeyAPI = errors.New(\"emulate error returned by KeysAPI.Get\")\n\n// table of test cases for method GetEntries\nvar getEntriesTestTable = []struct {\n\tinput getResult // value returned by the underlying etcd.KeysAPI.Get\n\tresp  []string  // response expected in output of GetEntries\n\terr   error     //error expected in output of GetEntries\n\n}{\n\t// test case: an error is returned by etcd.KeysAPI.Get\n\t{getResult{nil, errKeyAPI}, nil, errKeyAPI},\n\t// test case: return a single leaf node, with an empty value\n\t{getResult{&etcd.Response{\n\t\tAction: \"get\",\n\t\tNode: &etcd.Node{\n\t\t\tKey:           \"nodekey\",\n\t\t\tDir:           false,\n\t\t\tValue:         \"\",\n\t\t\tNodes:         nil,\n\t\t\tCreatedIndex:  0,\n\t\t\tModifiedIndex: 0,\n\t\t\tExpiration:    nil,\n\t\t\tTTL:           0,\n\t\t},\n\t\tPrevNode: nil,\n\t\tIndex:    0,\n\t}, nil}, []string{}, nil},\n\t// test case: return a single leaf node, with a value\n\t{getResult{&etcd.Response{\n\t\tAction: \"get\",\n\t\tNode: &etcd.Node{\n\t\t\tKey:           \"nodekey\",\n\t\t\tDir:           false,\n\t\t\tValue:         \"nodevalue\",\n\t\t\tNodes:         nil,\n\t\t\tCreatedIndex:  0,\n\t\t\tModifiedIndex: 0,\n\t\t\tExpiration:    nil,\n\t\t\tTTL:           0,\n\t\t},\n\t\tPrevNode: nil,\n\t\tIndex:    0,\n\t}, nil}, []string{\"nodevalue\"}, nil},\n\t// test case: return a node with two childs\n\t{getResult{&etcd.Response{\n\t\tAction: \"get\",\n\t\tNode: &etcd.Node{\n\t\t\tKey:   \"nodekey\",\n\t\t\tDir:   true,\n\t\t\tValue: \"nodevalue\",\n\t\t\tNodes: []*etcd.Node{\n\t\t\t\t{\n\t\t\t\t\tKey:           \"childnode1\",\n\t\t\t\t\tDir:           false,\n\t\t\t\t\tValue:         \"childvalue1\",\n\t\t\t\t\tNodes:         nil,\n\t\t\t\t\tCreatedIndex:  0,\n\t\t\t\t\tModifiedIndex: 0,\n\t\t\t\t\tExpiration:    nil,\n\t\t\t\t\tTTL:           0,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tKey:           \"childnode2\",\n\t\t\t\t\tDir:           false,\n\t\t\t\t\tValue:         \"childvalue2\",\n\t\t\t\t\tNodes:         nil,\n\t\t\t\t\tCreatedIndex:  0,\n\t\t\t\t\tModifiedIndex: 0,\n\t\t\t\t\tExpiration:    nil,\n\t\t\t\t\tTTL:           0,\n\t\t\t\t},\n\t\t\t},\n\t\t\tCreatedIndex:  0,\n\t\t\tModifiedIndex: 0,\n\t\t\tExpiration:    nil,\n\t\t\tTTL:           0,\n\t\t},\n\t\tPrevNode: nil,\n\t\tIndex:    0,\n\t}, nil}, []string{\"childvalue1\", \"childvalue2\"}, nil},\n}\n\nfunc TestGetEntries(t *testing.T) {\n\tfor _, et := range getEntriesTestTable {\n\t\tclient := newFakeClient(nil, nil, &et.input)\n\t\tresp, err := client.GetEntries(\"prefix\")\n\t\tif want, have := et.resp, resp; !reflect.DeepEqual(want, have) {\n\t\t\tt.Fatalf(\"want %v, have %v\", want, have)\n\t\t}\n\t\tif want, have := et.err, err; want != have {\n\t\t\tt.Fatalf(\"want %v, have %v\", want, have)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sd/etcd/doc.go",
    "content": "// Package etcd provides an Instancer and Registrar implementation for etcd. If\n// you use etcd as your service discovery system, this package will help you\n// implement the registration and client-side load balancing patterns.\npackage etcd\n"
  },
  {
    "path": "sd/etcd/example_test.go",
    "content": "package etcd\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/kit/sd/lb\"\n\t\"github.com/go-kit/log\"\n)\n\nfunc Example() {\n\t// Let's say this is a service that means to register itself.\n\t// First, we will set up some context.\n\tvar (\n\t\tetcdServer = \"http://10.0.0.1:2379\" // don't forget schema and port!\n\t\tprefix     = \"/services/foosvc/\"    // known at compile time\n\t\tinstance   = \"1.2.3.4:8080\"         // taken from runtime or platform, somehow\n\t\tkey        = prefix + instance      // should be globally unique\n\t\tvalue      = \"http://\" + instance   // based on our transport\n\t\tctx        = context.Background()\n\t)\n\n\t// Build the client.\n\tclient, err := NewClient(ctx, []string{etcdServer}, ClientOptions{})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Build the registrar.\n\tregistrar := NewRegistrar(client, Service{\n\t\tKey:   key,\n\t\tValue: value,\n\t}, log.NewNopLogger())\n\n\t// Register our instance.\n\tregistrar.Register()\n\n\t// At the end of our service lifecycle, for example at the end of func main,\n\t// we should make sure to deregister ourselves. This is important! Don't\n\t// accidentally skip this step by invoking a log.Fatal or os.Exit in the\n\t// interim, which bypasses the defer stack.\n\tdefer registrar.Deregister()\n\n\t// It's likely that we'll also want to connect to other services and call\n\t// their methods. We can build an Instancer to listen for changes from etcd,\n\t// create Endpointer, wrap it with a load-balancer to pick a single\n\t// endpoint, and finally wrap it with a retry strategy to get something that\n\t// can be used as an endpoint directly.\n\tbarPrefix := \"/services/barsvc\"\n\tlogger := log.NewNopLogger()\n\tinstancer, err := NewInstancer(client, barPrefix, logger)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tendpointer := sd.NewEndpointer(instancer, barFactory, logger)\n\tbalancer := lb.NewRoundRobin(endpointer)\n\tretry := lb.Retry(3, 3*time.Second, balancer)\n\n\t// And now retry can be used like any other endpoint.\n\treq := struct{}{}\n\tif _, err = retry(ctx, req); err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc barFactory(string) (endpoint.Endpoint, io.Closer, error) { return endpoint.Nop, nil, nil }\n"
  },
  {
    "path": "sd/etcd/instancer.go",
    "content": "package etcd\n\nimport (\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/kit/sd/internal/instance\"\n\t\"github.com/go-kit/log\"\n)\n\n// Instancer yields instances stored in a certain etcd keyspace. Any kind of\n// change in that keyspace is watched and will update the Instancer's Instancers.\ntype Instancer struct {\n\tcache  *instance.Cache\n\tclient Client\n\tprefix string\n\tlogger log.Logger\n\tquitc  chan struct{}\n}\n\n// NewInstancer returns an etcd instancer. It will start watching the given\n// prefix for changes, and update the subscribers.\nfunc NewInstancer(c Client, prefix string, logger log.Logger) (*Instancer, error) {\n\ts := &Instancer{\n\t\tclient: c,\n\t\tprefix: prefix,\n\t\tcache:  instance.NewCache(),\n\t\tlogger: logger,\n\t\tquitc:  make(chan struct{}),\n\t}\n\n\tinstances, err := s.client.GetEntries(s.prefix)\n\tif err == nil {\n\t\tlogger.Log(\"prefix\", s.prefix, \"instances\", len(instances))\n\t} else {\n\t\tlogger.Log(\"prefix\", s.prefix, \"err\", err)\n\t}\n\ts.cache.Update(sd.Event{Instances: instances, Err: err})\n\n\tgo s.loop()\n\treturn s, nil\n}\n\nfunc (s *Instancer) loop() {\n\tch := make(chan struct{})\n\tgo s.client.WatchPrefix(s.prefix, ch)\n\tfor {\n\t\tselect {\n\t\tcase <-ch:\n\t\t\tinstances, err := s.client.GetEntries(s.prefix)\n\t\t\tif err != nil {\n\t\t\t\ts.logger.Log(\"msg\", \"failed to retrieve entries\", \"err\", err)\n\t\t\t\ts.cache.Update(sd.Event{Err: err})\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ts.cache.Update(sd.Event{Instances: instances})\n\n\t\tcase <-s.quitc:\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// Stop terminates the Instancer.\nfunc (s *Instancer) Stop() {\n\tclose(s.quitc)\n}\n\n// Register implements Instancer.\nfunc (s *Instancer) Register(ch chan<- sd.Event) {\n\ts.cache.Register(ch)\n}\n\n// Deregister implements Instancer.\nfunc (s *Instancer) Deregister(ch chan<- sd.Event) {\n\ts.cache.Deregister(ch)\n}\n"
  },
  {
    "path": "sd/etcd/instancer_test.go",
    "content": "package etcd\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\tstdetcd \"go.etcd.io/etcd/client/v2\"\n\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/log\"\n)\n\nvar _ sd.Instancer = (*Instancer)(nil) // API check\n\nvar (\n\tnode = &stdetcd.Node{\n\t\tKey: \"/foo\",\n\t\tNodes: []*stdetcd.Node{\n\t\t\t{Key: \"/foo/1\", Value: \"1:1\"},\n\t\t\t{Key: \"/foo/2\", Value: \"1:2\"},\n\t\t},\n\t}\n\tfakeResponse = &stdetcd.Response{\n\t\tNode: node,\n\t}\n)\n\nvar _ sd.Instancer = &Instancer{} // API check\n\nfunc TestInstancer(t *testing.T) {\n\tclient := &fakeClient{\n\t\tresponses: map[string]*stdetcd.Response{\"/foo\": fakeResponse},\n\t}\n\n\ts, err := NewInstancer(client, \"/foo\", log.NewNopLogger())\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer s.Stop()\n\n\tif state := s.cache.State(); state.Err != nil {\n\t\tt.Fatal(state.Err)\n\t}\n}\n\ntype fakeClient struct {\n\tresponses map[string]*stdetcd.Response\n}\n\nfunc (c *fakeClient) GetEntries(prefix string) ([]string, error) {\n\tresponse, ok := c.responses[prefix]\n\tif !ok {\n\t\treturn nil, errors.New(\"key not exist\")\n\t}\n\n\tentries := make([]string, len(response.Node.Nodes))\n\tfor i, node := range response.Node.Nodes {\n\t\tentries[i] = node.Value\n\t}\n\treturn entries, nil\n}\n\nfunc (c *fakeClient) WatchPrefix(prefix string, ch chan struct{}) {}\n\nfunc (c *fakeClient) Register(Service) error {\n\treturn nil\n}\nfunc (c *fakeClient) Deregister(Service) error {\n\treturn nil\n}\n"
  },
  {
    "path": "sd/etcd/integration_test.go",
    "content": "//go:build flaky_integration\n// +build flaky_integration\n\npackage etcd\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/log\"\n)\n\n// Package sd/etcd provides a wrapper around the etcd key/value store. This\n// example assumes the user has an instance of etcd installed and running\n// locally on port 2379.\nfunc TestIntegration(t *testing.T) {\n\taddr := os.Getenv(\"ETCD_ADDR\")\n\tif addr == \"\" {\n\t\tt.Skip(\"ETCD_ADDR not set; skipping integration test\")\n\t}\n\n\tvar (\n\t\tprefix   = \"/services/foosvc/\" // known at compile time\n\t\tinstance = \"1.2.3.4:8080\"      // taken from runtime or platform, somehow\n\t\tkey      = prefix + instance\n\t\tvalue    = \"http://\" + instance // based on our transport\n\t)\n\n\tclient, err := NewClient(context.Background(), []string{addr}, ClientOptions{\n\t\tDialTimeout:             2 * time.Second,\n\t\tDialKeepAlive:           2 * time.Second,\n\t\tHeaderTimeoutPerRequest: 2 * time.Second,\n\t})\n\tif err != nil {\n\t\tt.Fatalf(\"NewClient(%q): %v\", addr, err)\n\t}\n\n\t// Verify test data is initially empty.\n\tentries, err := client.GetEntries(key)\n\tif err == nil {\n\t\tt.Fatalf(\"GetEntries(%q): expected error, got none\", key)\n\t}\n\tt.Logf(\"GetEntries(%q): %v (OK)\", key, err)\n\n\t// Instantiate a new Registrar, passing in test data.\n\tregistrar := NewRegistrar(client, Service{\n\t\tKey:   key,\n\t\tValue: value,\n\t}, log.With(log.NewLogfmtLogger(os.Stderr), \"component\", \"registrar\"))\n\n\t// Register our instance.\n\tregistrar.Register()\n\tt.Logf(\"Registered\")\n\n\t// Retrieve entries from etcd manually.\n\tentries, err = client.GetEntries(key)\n\tif err != nil {\n\t\tt.Fatalf(\"client.GetEntries(%q): %v\", key, err)\n\t}\n\tif want, have := 1, len(entries); want != have {\n\t\tt.Fatalf(\"client.GetEntries(%q): want %d, have %d\", key, want, have)\n\t}\n\tif want, have := value, entries[0]; want != have {\n\t\tt.Fatalf(\"want %q, have %q\", want, have)\n\t}\n\n\tinstancer, err := NewInstancer(\n\t\tclient,\n\t\tprefix,\n\t\tlog.With(log.NewLogfmtLogger(os.Stderr), \"component\", \"instancer\"),\n\t)\n\tif err != nil {\n\t\tt.Fatalf(\"NewInstancer: %v\", err)\n\t}\n\tendpointer := sd.NewEndpointer(\n\t\tinstancer,\n\t\tfunc(string) (endpoint.Endpoint, io.Closer, error) { return endpoint.Nop, nil, nil },\n\t\tlog.With(log.NewLogfmtLogger(os.Stderr), \"component\", \"instancer\"),\n\t)\n\tt.Logf(\"Constructed Endpointer OK\")\n\n\tif !within(time.Second, func() bool {\n\t\tendpoints, err := endpointer.Endpoints()\n\t\treturn err == nil && len(endpoints) == 1\n\t}) {\n\t\tt.Fatalf(\"Endpointer didn't see Register in time\")\n\t}\n\tt.Logf(\"Endpointer saw Register OK\")\n\n\t// Deregister first instance of test data.\n\tregistrar.Deregister()\n\tt.Logf(\"Deregistered\")\n\n\t// Check it was deregistered.\n\tif !within(time.Second, func() bool {\n\t\tendpoints, err := endpointer.Endpoints()\n\t\tt.Logf(\"Checking Deregister: len(endpoints) = %d, err = %v\", len(endpoints), err)\n\t\treturn err == nil && len(endpoints) == 0\n\t}) {\n\t\tt.Fatalf(\"Endpointer didn't see Deregister in time\")\n\t}\n\n\t// Verify test data no longer exists in etcd.\n\t_, err = client.GetEntries(key)\n\tif err == nil {\n\t\tt.Fatalf(\"GetEntries(%q): expected error, got none\", key)\n\t}\n\tt.Logf(\"GetEntries(%q): %v (OK)\", key, err)\n}\n\nfunc within(d time.Duration, f func() bool) bool {\n\tdeadline := time.Now().Add(d)\n\tfor time.Now().Before(deadline) {\n\t\tif f() {\n\t\t\treturn true\n\t\t}\n\t\ttime.Sleep(d / 10)\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "sd/etcd/registrar.go",
    "content": "package etcd\n\nimport (\n\t\"sync\"\n\t\"time\"\n\n\tetcd \"go.etcd.io/etcd/client/v2\"\n\n\t\"github.com/go-kit/log\"\n)\n\nconst minHeartBeatTime = 500 * time.Millisecond\n\n// Registrar registers service instance liveness information to etcd.\ntype Registrar struct {\n\tclient  Client\n\tservice Service\n\tlogger  log.Logger\n\n\tquitmtx sync.Mutex\n\tquit    chan struct{}\n}\n\n// Service holds the instance identifying data you want to publish to etcd. Key\n// must be unique, and value is the string returned to subscribers, typically\n// called the \"instance\" string in other parts of package sd.\ntype Service struct {\n\tKey           string // unique key, e.g. \"/service/foobar/1.2.3.4:8080\"\n\tValue         string // returned to subscribers, e.g. \"http://1.2.3.4:8080\"\n\tTTL           *TTLOption\n\tDeleteOptions *etcd.DeleteOptions\n}\n\n// TTLOption allow setting a key with a TTL. This option will be used by a loop\n// goroutine which regularly refreshes the lease of the key.\ntype TTLOption struct {\n\theartbeat time.Duration // e.g. time.Second * 3\n\tttl       time.Duration // e.g. time.Second * 10\n}\n\n// NewTTLOption returns a TTLOption that contains proper TTL settings. Heartbeat\n// is used to refresh the lease of the key periodically; its value should be at\n// least 500ms. TTL defines the lease of the key; its value should be\n// significantly greater than heartbeat.\n//\n// Good default values might be 3s heartbeat, 10s TTL.\nfunc NewTTLOption(heartbeat, ttl time.Duration) *TTLOption {\n\tif heartbeat <= minHeartBeatTime {\n\t\theartbeat = minHeartBeatTime\n\t}\n\tif ttl <= heartbeat {\n\t\tttl = 3 * heartbeat\n\t}\n\treturn &TTLOption{\n\t\theartbeat: heartbeat,\n\t\tttl:       ttl,\n\t}\n}\n\n// NewRegistrar returns a etcd Registrar acting on the provided catalog\n// registration (service).\nfunc NewRegistrar(client Client, service Service, logger log.Logger) *Registrar {\n\treturn &Registrar{\n\t\tclient:  client,\n\t\tservice: service,\n\t\tlogger:  log.With(logger, \"key\", service.Key, \"value\", service.Value),\n\t}\n}\n\n// Register implements the sd.Registrar interface. Call it when you want your\n// service to be registered in etcd, typically at startup.\nfunc (r *Registrar) Register() {\n\tif err := r.client.Register(r.service); err != nil {\n\t\tr.logger.Log(\"err\", err)\n\t} else {\n\t\tr.logger.Log(\"action\", \"register\")\n\t}\n\tif r.service.TTL != nil {\n\t\tgo r.loop()\n\t}\n}\n\nfunc (r *Registrar) loop() {\n\tr.quitmtx.Lock()\n\tif r.quit != nil {\n\t\treturn // already running\n\t}\n\tr.quit = make(chan struct{})\n\tr.quitmtx.Unlock()\n\n\ttick := time.NewTicker(r.service.TTL.heartbeat)\n\tdefer tick.Stop()\n\tfor {\n\t\tselect {\n\t\tcase <-tick.C:\n\t\t\tif err := r.client.Register(r.service); err != nil {\n\t\t\t\tr.logger.Log(\"err\", err)\n\t\t\t}\n\t\tcase <-r.quit:\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// Deregister implements the sd.Registrar interface. Call it when you want your\n// service to be deregistered from etcd, typically just prior to shutdown.\nfunc (r *Registrar) Deregister() {\n\tif err := r.client.Deregister(r.service); err != nil {\n\t\tr.logger.Log(\"err\", err)\n\t} else {\n\t\tr.logger.Log(\"action\", \"deregister\")\n\t}\n\n\tr.quitmtx.Lock()\n\tdefer r.quitmtx.Unlock()\n\tif r.quit != nil {\n\t\tclose(r.quit)\n\t\tr.quit = nil\n\t}\n}\n"
  },
  {
    "path": "sd/etcd/registrar_test.go",
    "content": "package etcd\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/go-kit/log\"\n)\n\n// testClient is a basic implementation of Client\ntype testClient struct {\n\tregisterRes error // value returned when Register or Deregister is called\n}\n\nfunc (tc *testClient) GetEntries(prefix string) ([]string, error) {\n\treturn nil, nil\n}\n\nfunc (tc *testClient) WatchPrefix(prefix string, ch chan struct{}) {\n\treturn\n}\n\nfunc (tc *testClient) Register(s Service) error {\n\treturn tc.registerRes\n}\n\nfunc (tc *testClient) Deregister(s Service) error {\n\treturn tc.registerRes\n}\n\n// default service used to build registrar in our tests\nvar testService = Service{\"testKey\", \"testValue\", nil, nil}\n\n// NewRegistar should return a registar with a logger using the service key and value\nfunc TestNewRegistar(t *testing.T) {\n\tc := Client(&testClient{nil})\n\tbuf := &bytes.Buffer{}\n\tlogger := log.NewLogfmtLogger(buf)\n\tr := NewRegistrar(\n\t\tc,\n\t\ttestService,\n\t\tlogger,\n\t)\n\n\tif err := r.logger.Log(\"msg\", \"message\"); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif want, have := \"key=testKey value=testValue msg=message\\n\", buf.String(); want != have {\n\t\tt.Errorf(\"\\nwant: %shave: %s\", want, have)\n\t}\n}\n\n// Register log the error returned by the client or log the successful registration action\n// table of test cases for method Register\nvar registerTestTable = []struct {\n\tregisterRes error  // value returned by the client on calls to Register\n\tlog         string // expected log by the registrar\n\n}{\n\t// test case: an error is returned by the client\n\t{errors.New(\"regError\"), \"key=testKey value=testValue err=regError\\n\"},\n\t// test case: registration successful\n\t{nil, \"key=testKey value=testValue action=register\\n\"},\n}\n\nfunc TestRegister(t *testing.T) {\n\tfor _, tc := range registerTestTable {\n\t\tc := Client(&testClient{tc.registerRes})\n\t\tbuf := &bytes.Buffer{}\n\t\tlogger := log.NewLogfmtLogger(buf)\n\t\tr := NewRegistrar(\n\t\t\tc,\n\t\t\ttestService,\n\t\t\tlogger,\n\t\t)\n\t\tr.Register()\n\t\tif want, have := tc.log, buf.String(); want != have {\n\t\t\tt.Fatalf(\"want %v, have %v\", want, have)\n\t\t}\n\t}\n}\n\n// Deregister log the error returned by the client or log the successful deregistration action\n// table of test cases for method Deregister\nvar deregisterTestTable = []struct {\n\tderegisterRes error  // value returned by the client on calls to Deregister\n\tlog           string // expected log by the registrar\n}{\n\t// test case: an error is returned by the client\n\t{errors.New(\"deregError\"), \"key=testKey value=testValue err=deregError\\n\"},\n\t// test case: deregistration successful\n\t{nil, \"key=testKey value=testValue action=deregister\\n\"},\n}\n\nfunc TestDeregister(t *testing.T) {\n\tfor _, tc := range deregisterTestTable {\n\t\tc := Client(&testClient{tc.deregisterRes})\n\t\tbuf := &bytes.Buffer{}\n\t\tlogger := log.NewLogfmtLogger(buf)\n\t\tr := NewRegistrar(\n\t\t\tc,\n\t\t\ttestService,\n\t\t\tlogger,\n\t\t)\n\t\tr.Deregister()\n\t\tif want, have := tc.log, buf.String(); want != have {\n\t\t\tt.Fatalf(\"want %v, have %v\", want, have)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sd/etcdv3/client.go",
    "content": "package etcdv3\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"time\"\n\n\t\"go.etcd.io/etcd/client/pkg/v3/transport\"\n\tclientv3 \"go.etcd.io/etcd/client/v3\"\n\t\"google.golang.org/grpc\"\n)\n\nvar (\n\t// ErrNoKey indicates a client method needs a key but receives none.\n\tErrNoKey = errors.New(\"no key provided\")\n\n\t// ErrNoValue indicates a client method needs a value but receives none.\n\tErrNoValue = errors.New(\"no value provided\")\n)\n\n// Client is a wrapper around the etcd client.\ntype Client interface {\n\t// GetEntries queries the given prefix in etcd and returns a slice\n\t// containing the values of all keys found, recursively, underneath that\n\t// prefix.\n\tGetEntries(prefix string) ([]string, error)\n\n\t// WatchPrefix watches the given prefix in etcd for changes. When a change\n\t// is detected, it will signal on the passed channel. Clients are expected\n\t// to call GetEntries to update themselves with the latest set of complete\n\t// values. WatchPrefix will always send an initial sentinel value on the\n\t// channel after establishing the watch, to ensure that clients always\n\t// receive the latest set of values. WatchPrefix will block until the\n\t// context passed to the NewClient constructor is terminated.\n\tWatchPrefix(prefix string, ch chan struct{})\n\n\t// Register a service with etcd.\n\tRegister(s Service) error\n\n\t// Deregister a service with etcd.\n\tDeregister(s Service) error\n\n\t// LeaseID returns the lease id created for this service instance\n\tLeaseID() int64\n}\n\ntype client struct {\n\tcli *clientv3.Client\n\tctx context.Context\n\n\tkv clientv3.KV\n\n\t// Watcher interface instance, used to leverage Watcher.Close()\n\twatcher clientv3.Watcher\n\t// watcher context\n\twctx context.Context\n\t// watcher cancel func\n\twcf context.CancelFunc\n\n\t// leaseID will be 0 (clientv3.NoLease) if a lease was not created\n\tleaseID clientv3.LeaseID\n\n\thbch <-chan *clientv3.LeaseKeepAliveResponse\n\t// Lease interface instance, used to leverage Lease.Close()\n\tleaser clientv3.Lease\n}\n\n// ClientOptions defines options for the etcd client. All values are optional.\n// If any duration is not specified, a default of 3 seconds will be used.\ntype ClientOptions struct {\n\tCert          string\n\tKey           string\n\tCACert        string\n\tDialTimeout   time.Duration\n\tDialKeepAlive time.Duration\n\n\t// DialOptions is a list of dial options for the gRPC client (e.g., for interceptors).\n\t// For example, pass grpc.WithBlock() to block until the underlying connection is up.\n\t// Without this, Dial returns immediately and connecting the server happens in background.\n\tDialOptions []grpc.DialOption\n\n\tUsername string\n\tPassword string\n}\n\n// NewClient returns Client with a connection to the named machines. It will\n// return an error if a connection to the cluster cannot be made.\nfunc NewClient(ctx context.Context, machines []string, options ClientOptions) (Client, error) {\n\tif options.DialTimeout == 0 {\n\t\toptions.DialTimeout = 3 * time.Second\n\t}\n\tif options.DialKeepAlive == 0 {\n\t\toptions.DialKeepAlive = 3 * time.Second\n\t}\n\n\tvar err error\n\tvar tlscfg *tls.Config\n\n\tif options.Cert != \"\" && options.Key != \"\" {\n\t\ttlsInfo := transport.TLSInfo{\n\t\t\tCertFile:      options.Cert,\n\t\t\tKeyFile:       options.Key,\n\t\t\tTrustedCAFile: options.CACert,\n\t\t}\n\t\ttlscfg, err = tlsInfo.ClientConfig()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tcli, err := clientv3.New(clientv3.Config{\n\t\tContext:           ctx,\n\t\tEndpoints:         machines,\n\t\tDialTimeout:       options.DialTimeout,\n\t\tDialKeepAliveTime: options.DialKeepAlive,\n\t\tDialOptions:       options.DialOptions,\n\t\tTLS:               tlscfg,\n\t\tUsername:          options.Username,\n\t\tPassword:          options.Password,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &client{\n\t\tcli: cli,\n\t\tctx: ctx,\n\t\tkv:  clientv3.NewKV(cli),\n\t}, nil\n}\n\nfunc (c *client) LeaseID() int64 { return int64(c.leaseID) }\n\n// GetEntries implements the etcd Client interface.\nfunc (c *client) GetEntries(key string) ([]string, error) {\n\tresp, err := c.kv.Get(c.ctx, key, clientv3.WithPrefix())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tentries := make([]string, len(resp.Kvs))\n\tfor i, kv := range resp.Kvs {\n\t\tentries[i] = string(kv.Value)\n\t}\n\n\treturn entries, nil\n}\n\n// WatchPrefix implements the etcd Client interface.\nfunc (c *client) WatchPrefix(prefix string, ch chan struct{}) {\n\tc.wctx, c.wcf = context.WithCancel(c.ctx)\n\tc.watcher = clientv3.NewWatcher(c.cli)\n\n\twch := c.watcher.Watch(c.wctx, prefix, clientv3.WithPrefix(), clientv3.WithRev(0))\n\tch <- struct{}{}\n\tfor wr := range wch {\n\t\tif wr.Canceled {\n\t\t\treturn\n\t\t}\n\t\tch <- struct{}{}\n\t}\n}\n\nfunc (c *client) Register(s Service) error {\n\tvar err error\n\n\tif s.Key == \"\" {\n\t\treturn ErrNoKey\n\t}\n\tif s.Value == \"\" {\n\t\treturn ErrNoValue\n\t}\n\n\tif c.leaser != nil {\n\t\tc.leaser.Close()\n\t}\n\tc.leaser = clientv3.NewLease(c.cli)\n\n\tif c.watcher != nil {\n\t\tc.watcher.Close()\n\t}\n\tc.watcher = clientv3.NewWatcher(c.cli)\n\tif c.kv == nil {\n\t\tc.kv = clientv3.NewKV(c.cli)\n\t}\n\n\tif s.TTL == nil {\n\t\ts.TTL = NewTTLOption(time.Second*3, time.Second*10)\n\t}\n\n\tgrantResp, err := c.leaser.Grant(c.ctx, int64(s.TTL.ttl.Seconds()))\n\tif err != nil {\n\t\treturn err\n\t}\n\tc.leaseID = grantResp.ID\n\n\t_, err = c.kv.Put(\n\t\tc.ctx,\n\t\ts.Key,\n\t\ts.Value,\n\t\tclientv3.WithLease(c.leaseID),\n\t)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// this will keep the key alive 'forever' or until we revoke it or\n\t// the context is canceled\n\tc.hbch, err = c.leaser.KeepAlive(c.ctx, c.leaseID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// discard the keepalive response, make etcd library not to complain\n\t// fix bug #799\n\tgo func() {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase r := <-c.hbch:\n\t\t\t\t// avoid dead loop when channel was closed\n\t\t\t\tif r == nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\tcase <-c.ctx.Done():\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\n\treturn nil\n}\n\nfunc (c *client) Deregister(s Service) error {\n\tdefer c.close()\n\n\tif s.Key == \"\" {\n\t\treturn ErrNoKey\n\t}\n\tif _, err := c.cli.Delete(c.ctx, s.Key, clientv3.WithIgnoreLease()); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// close will close any open clients and call\n// the watcher cancel func\nfunc (c *client) close() {\n\tif c.leaser != nil {\n\t\tc.leaser.Close()\n\t}\n\tif c.watcher != nil {\n\t\tc.watcher.Close()\n\t}\n\tif c.wcf != nil {\n\t\tc.wcf()\n\t}\n}\n"
  },
  {
    "path": "sd/etcdv3/client_test.go",
    "content": "package etcdv3\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"google.golang.org/grpc\"\n)\n\nconst (\n\t// irrelevantEndpoint is an address which does not exists.\n\tirrelevantEndpoint = \"http://irrelevant:12345\"\n)\n\nfunc TestNewClient(t *testing.T) {\n\tclient, err := NewClient(\n\t\tcontext.Background(),\n\t\t[]string{irrelevantEndpoint},\n\t\tClientOptions{\n\t\t\tDialTimeout:   3 * time.Second,\n\t\t\tDialKeepAlive: 3 * time.Second,\n\t\t},\n\t)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error creating client: %v\", err)\n\t}\n\tif client == nil {\n\t\tt.Fatal(\"expected new Client, got nil\")\n\t}\n}\n\nfunc TestClientOptions(t *testing.T) {\n\tclient, err := NewClient(\n\t\tcontext.Background(),\n\t\t[]string{},\n\t\tClientOptions{\n\t\t\tCert:          \"\",\n\t\t\tKey:           \"\",\n\t\t\tCACert:        \"\",\n\t\t\tDialTimeout:   3 * time.Second,\n\t\t\tDialKeepAlive: 3 * time.Second,\n\t\t},\n\t)\n\tif err == nil {\n\t\tt.Errorf(\"expected error: %v\", err)\n\t}\n\tif client != nil {\n\t\tt.Fatalf(\"expected client to be nil on failure\")\n\t}\n\n\t_, err = NewClient(\n\t\tcontext.Background(),\n\t\t[]string{irrelevantEndpoint},\n\t\tClientOptions{\n\t\t\tCert:          \"does-not-exist.crt\",\n\t\t\tKey:           \"does-not-exist.key\",\n\t\t\tCACert:        \"does-not-exist.CACert\",\n\t\t\tDialTimeout:   3 * time.Second,\n\t\t\tDialKeepAlive: 3 * time.Second,\n\t\t},\n\t)\n\tif err == nil {\n\t\tt.Errorf(\"expected error: %v\", err)\n\t}\n\n\tclient, err = NewClient(\n\t\tcontext.Background(),\n\t\t[]string{irrelevantEndpoint},\n\t\tClientOptions{\n\t\t\tDialOptions: []grpc.DialOption{grpc.WithBlock()},\n\t\t},\n\t)\n\tif err == nil {\n\t\tt.Errorf(\"expected connection should fail\")\n\t}\n\tif client != nil {\n\t\tt.Errorf(\"expected client to be nil on failure\")\n\t}\n}\n"
  },
  {
    "path": "sd/etcdv3/doc.go",
    "content": "// Package etcdv3 provides an Instancer and Registrar implementation for etcd v3. If\n// you use etcd v3 as your service discovery system, this package will help you\n// implement the registration and client-side load balancing patterns.\npackage etcdv3\n"
  },
  {
    "path": "sd/etcdv3/example_test.go",
    "content": "package etcdv3\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/kit/sd/lb\"\n\t\"github.com/go-kit/log\"\n\t\"google.golang.org/grpc\"\n)\n\nfunc Example() {\n\t// Let's say this is a service that means to register itself.\n\t// First, we will set up some context.\n\tvar (\n\t\tetcdServer = \"10.0.0.1:2379\"      // in the change from v2 to v3, the schema is no longer necessary if connecting directly to an etcd v3 instance\n\t\tprefix     = \"/services/foosvc/\"  // known at compile time\n\t\tinstance   = \"1.2.3.4:8080\"       // taken from runtime or platform, somehow\n\t\tkey        = prefix + instance    // should be globally unique\n\t\tvalue      = \"http://\" + instance // based on our transport\n\t\tctx        = context.Background()\n\t)\n\n\toptions := ClientOptions{\n\t\t// Path to trusted ca file\n\t\tCACert: \"\",\n\n\t\t// Path to certificate\n\t\tCert: \"\",\n\n\t\t// Path to private key\n\t\tKey: \"\",\n\n\t\t// Username if required\n\t\tUsername: \"\",\n\n\t\t// Password if required\n\t\tPassword: \"\",\n\n\t\t// If DialTimeout is 0, it defaults to 3s\n\t\tDialTimeout: time.Second * 3,\n\n\t\t// If DialKeepAlive is 0, it defaults to 3s\n\t\tDialKeepAlive: time.Second * 3,\n\n\t\t// If passing `grpc.WithBlock`, dial connection will block until success.\n\t\tDialOptions: []grpc.DialOption{grpc.WithBlock()},\n\t}\n\n\t// Build the client.\n\tclient, err := NewClient(ctx, []string{etcdServer}, options)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Build the registrar.\n\tregistrar := NewRegistrar(client, Service{\n\t\tKey:   key,\n\t\tValue: value,\n\t}, log.NewNopLogger())\n\n\t// Register our instance.\n\tregistrar.Register()\n\n\t// At the end of our service lifecycle, for example at the end of func main,\n\t// we should make sure to deregister ourselves. This is important! Don't\n\t// accidentally skip this step by invoking a log.Fatal or os.Exit in the\n\t// interim, which bypasses the defer stack.\n\tdefer registrar.Deregister()\n\n\t// It's likely that we'll also want to connect to other services and call\n\t// their methods. We can build an Instancer to listen for changes from etcd,\n\t// create Endpointer, wrap it with a load-balancer to pick a single\n\t// endpoint, and finally wrap it with a retry strategy to get something that\n\t// can be used as an endpoint directly.\n\tbarPrefix := \"/services/barsvc\"\n\tlogger := log.NewNopLogger()\n\tinstancer, err := NewInstancer(client, barPrefix, logger)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tendpointer := sd.NewEndpointer(instancer, barFactory, logger)\n\tbalancer := lb.NewRoundRobin(endpointer)\n\tretry := lb.Retry(3, 3*time.Second, balancer)\n\n\t// And now retry can be used like any other endpoint.\n\treq := struct{}{}\n\tif _, err = retry(ctx, req); err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc barFactory(string) (endpoint.Endpoint, io.Closer, error) { return endpoint.Nop, nil, nil }\n"
  },
  {
    "path": "sd/etcdv3/instancer.go",
    "content": "package etcdv3\n\nimport (\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/kit/sd/internal/instance\"\n\t\"github.com/go-kit/log\"\n)\n\n// Instancer yields instances stored in a certain etcd keyspace. Any kind of\n// change in that keyspace is watched and will update the Instancer's Instancers.\ntype Instancer struct {\n\tcache  *instance.Cache\n\tclient Client\n\tprefix string\n\tlogger log.Logger\n\tquitc  chan struct{}\n}\n\n// NewInstancer returns an etcd instancer. It will start watching the given\n// prefix for changes, and update the subscribers.\nfunc NewInstancer(c Client, prefix string, logger log.Logger) (*Instancer, error) {\n\ts := &Instancer{\n\t\tclient: c,\n\t\tprefix: prefix,\n\t\tcache:  instance.NewCache(),\n\t\tlogger: logger,\n\t\tquitc:  make(chan struct{}),\n\t}\n\n\tinstances, err := s.client.GetEntries(s.prefix)\n\tif err == nil {\n\t\tlogger.Log(\"prefix\", s.prefix, \"instances\", len(instances))\n\t} else {\n\t\tlogger.Log(\"prefix\", s.prefix, \"err\", err)\n\t}\n\ts.cache.Update(sd.Event{Instances: instances, Err: err})\n\n\tgo s.loop()\n\treturn s, nil\n}\n\nfunc (s *Instancer) loop() {\n\tch := make(chan struct{})\n\tgo s.client.WatchPrefix(s.prefix, ch)\n\n\tfor {\n\t\tselect {\n\t\tcase <-ch:\n\t\t\tinstances, err := s.client.GetEntries(s.prefix)\n\t\t\tif err != nil {\n\t\t\t\ts.logger.Log(\"msg\", \"failed to retrieve entries\", \"err\", err)\n\t\t\t\ts.cache.Update(sd.Event{Err: err})\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ts.cache.Update(sd.Event{Instances: instances})\n\n\t\tcase <-s.quitc:\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// Stop terminates the Instancer.\nfunc (s *Instancer) Stop() {\n\tclose(s.quitc)\n}\n\n// Register implements Instancer.\nfunc (s *Instancer) Register(ch chan<- sd.Event) {\n\ts.cache.Register(ch)\n}\n\n// Deregister implements Instancer.\nfunc (s *Instancer) Deregister(ch chan<- sd.Event) {\n\ts.cache.Deregister(ch)\n}\n"
  },
  {
    "path": "sd/etcdv3/instancer_test.go",
    "content": "package etcdv3\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/log\"\n)\n\nvar _ sd.Instancer = (*Instancer)(nil) // API check\n\ntype testKV struct {\n\tKey   []byte\n\tValue []byte\n}\n\ntype testResponse struct {\n\tKvs []testKV\n}\n\nvar (\n\tfakeResponse = testResponse{\n\t\tKvs: []testKV{\n\t\t\t{\n\t\t\t\tKey:   []byte(\"/foo/1\"),\n\t\t\t\tValue: []byte(\"1:1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\tKey:   []byte(\"/foo/2\"),\n\t\t\t\tValue: []byte(\"2:2\"),\n\t\t\t},\n\t\t},\n\t}\n)\n\nvar _ sd.Instancer = &Instancer{} // API check\n\nfunc TestInstancer(t *testing.T) {\n\tclient := &fakeClient{\n\t\tresponses: map[string]testResponse{\"/foo\": fakeResponse},\n\t}\n\n\ts, err := NewInstancer(client, \"/foo\", log.NewNopLogger())\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer s.Stop()\n\n\tif state := s.cache.State(); state.Err != nil {\n\t\tt.Fatal(state.Err)\n\t}\n}\n\ntype fakeClient struct {\n\tresponses map[string]testResponse\n}\n\nfunc (c *fakeClient) GetEntries(prefix string) ([]string, error) {\n\tresponse, ok := c.responses[prefix]\n\tif !ok {\n\t\treturn nil, errors.New(\"key not exist\")\n\t}\n\n\tentries := make([]string, len(response.Kvs))\n\tfor i, node := range response.Kvs {\n\t\tentries[i] = string(node.Value)\n\t}\n\treturn entries, nil\n}\n\nfunc (c *fakeClient) WatchPrefix(prefix string, ch chan struct{}) {\n}\n\nfunc (c *fakeClient) LeaseID() int64 {\n\treturn 0\n}\n\nfunc (c *fakeClient) Register(Service) error {\n\treturn nil\n}\nfunc (c *fakeClient) Deregister(Service) error {\n\treturn nil\n}\n"
  },
  {
    "path": "sd/etcdv3/integration_test.go",
    "content": "//go:build flaky_integration\n// +build flaky_integration\n\npackage etcdv3\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/log\"\n)\n\nfunc runIntegration(settings integrationSettings, client Client, service Service, t *testing.T) {\n\t// Verify test data is initially empty.\n\tentries, err := client.GetEntries(settings.key)\n\tif err != nil {\n\t\tt.Fatalf(\"GetEntries(%q): expected no error, got one: %v\", settings.key, err)\n\t}\n\tif len(entries) > 0 {\n\t\tt.Fatalf(\"GetEntries(%q): expected no instance entries, got %d\", settings.key, len(entries))\n\t}\n\tt.Logf(\"GetEntries(%q): %v (OK)\", settings.key, entries)\n\n\t// Instantiate a new Registrar, passing in test data.\n\tregistrar := NewRegistrar(\n\t\tclient,\n\t\tservice,\n\t\tlog.With(log.NewLogfmtLogger(os.Stderr), \"component\", \"registrar\"),\n\t)\n\n\t// Register our instance.\n\tregistrar.Register()\n\tt.Log(\"Registered\")\n\n\t// Retrieve entries from etcd manually.\n\tentries, err = client.GetEntries(settings.key)\n\tif err != nil {\n\t\tt.Fatalf(\"client.GetEntries(%q): %v\", settings.key, err)\n\t}\n\tif want, have := 1, len(entries); want != have {\n\t\tt.Fatalf(\"client.GetEntries(%q): want %d, have %d\", settings.key, want, have)\n\t}\n\tif want, have := settings.value, entries[0]; want != have {\n\t\tt.Fatalf(\"want %q, have %q\", want, have)\n\t}\n\n\tinstancer, err := NewInstancer(\n\t\tclient,\n\t\tsettings.prefix,\n\t\tlog.With(log.NewLogfmtLogger(os.Stderr), \"component\", \"instancer\"),\n\t)\n\tif err != nil {\n\t\tt.Fatalf(\"NewInstancer: %v\", err)\n\t}\n\tt.Log(\"Constructed Instancer OK\")\n\tdefer instancer.Stop()\n\n\tendpointer := sd.NewEndpointer(\n\t\tinstancer,\n\t\tfunc(string) (endpoint.Endpoint, io.Closer, error) { return endpoint.Nop, nil, nil },\n\t\tlog.With(log.NewLogfmtLogger(os.Stderr), \"component\", \"instancer\"),\n\t)\n\tt.Log(\"Constructed Endpointer OK\")\n\tdefer endpointer.Close()\n\n\tif !within(time.Second, func() bool {\n\t\tendpoints, err := endpointer.Endpoints()\n\t\treturn err == nil && len(endpoints) == 1\n\t}) {\n\t\tt.Fatal(\"Endpointer didn't see Register in time\")\n\t}\n\tt.Log(\"Endpointer saw Register OK\")\n\n\t// Deregister first instance of test data.\n\tregistrar.Deregister()\n\tt.Log(\"Deregistered\")\n\n\t// Check it was deregistered.\n\tif !within(time.Second, func() bool {\n\t\tendpoints, err := endpointer.Endpoints()\n\t\tt.Logf(\"Checking Deregister: len(endpoints) = %d, err = %v\", len(endpoints), err)\n\t\treturn err == nil && len(endpoints) == 0\n\t}) {\n\t\tt.Fatalf(\"Endpointer didn't see Deregister in time\")\n\t}\n\n\t// Verify test data no longer exists in etcd.\n\tentries, err = client.GetEntries(settings.key)\n\tif err != nil {\n\t\tt.Fatalf(\"GetEntries(%q): expected no error, got one: %v\", settings.key, err)\n\t}\n\tif len(entries) > 0 {\n\t\tt.Fatalf(\"GetEntries(%q): expected no entries, got %v\", settings.key, entries)\n\t}\n\tt.Logf(\"GetEntries(%q): %v (OK)\", settings.key, entries)\n}\n\ntype integrationSettings struct {\n\taddr     string\n\tprefix   string\n\tinstance string\n\tkey      string\n\tvalue    string\n}\n\nfunc testIntegrationSettings(t *testing.T) integrationSettings {\n\tvar settings integrationSettings\n\n\tsettings.addr = os.Getenv(\"ETCD_ADDR\")\n\tif settings.addr == \"\" {\n\t\tt.Skip(\"ETCD_ADDR not set; skipping integration test\")\n\t}\n\n\tsettings.prefix = \"/services/foosvc/\" // known at compile time\n\tsettings.instance = \"1.2.3.4:8080\"    // taken from runtime or platform, somehow\n\tsettings.key = settings.prefix + settings.instance\n\tsettings.value = \"http://\" + settings.instance // based on our transport\n\n\treturn settings\n}\n\n// Package sd/etcd provides a wrapper around the etcd key/value store. This\n// example assumes the user has an instance of etcd installed and running\n// locally on port 2379.\nfunc TestIntegration(t *testing.T) {\n\tsettings := testIntegrationSettings(t)\n\tclient, err := NewClient(context.Background(), []string{settings.addr}, ClientOptions{\n\t\tDialTimeout:   2 * time.Second,\n\t\tDialKeepAlive: 2 * time.Second,\n\t})\n\tif err != nil {\n\t\tt.Fatalf(\"NewClient(%q): %v\", settings.addr, err)\n\t}\n\n\tservice := Service{\n\t\tKey:   settings.key,\n\t\tValue: settings.value,\n\t}\n\n\trunIntegration(settings, client, service, t)\n}\n\nfunc TestIntegrationTTL(t *testing.T) {\n\tsettings := testIntegrationSettings(t)\n\tclient, err := NewClient(context.Background(), []string{settings.addr}, ClientOptions{\n\t\tDialTimeout:   2 * time.Second,\n\t\tDialKeepAlive: 2 * time.Second,\n\t})\n\tif err != nil {\n\t\tt.Fatalf(\"NewClient(%q): %v\", settings.addr, err)\n\t}\n\n\tservice := Service{\n\t\tKey:   settings.key,\n\t\tValue: settings.value,\n\t\tTTL:   NewTTLOption(time.Second*3, time.Second*10),\n\t}\n\tdefer client.Deregister(service)\n\n\trunIntegration(settings, client, service, t)\n}\n\nfunc TestIntegrationRegistrarOnly(t *testing.T) {\n\tsettings := testIntegrationSettings(t)\n\tclient, err := NewClient(context.Background(), []string{settings.addr}, ClientOptions{\n\t\tDialTimeout:   2 * time.Second,\n\t\tDialKeepAlive: 2 * time.Second,\n\t})\n\tif err != nil {\n\t\tt.Fatalf(\"NewClient(%q): %v\", settings.addr, err)\n\t}\n\n\tservice := Service{\n\t\tKey:   settings.key,\n\t\tValue: settings.value,\n\t\tTTL:   NewTTLOption(time.Second*3, time.Second*10),\n\t}\n\tdefer client.Deregister(service)\n\n\t// Verify test data is initially empty.\n\tentries, err := client.GetEntries(settings.key)\n\tif err != nil {\n\t\tt.Fatalf(\"GetEntries(%q): expected no error, got one: %v\", settings.key, err)\n\t}\n\tif len(entries) > 0 {\n\t\tt.Fatalf(\"GetEntries(%q): expected no instance entries, got %d\", settings.key, len(entries))\n\t}\n\tt.Logf(\"GetEntries(%q): %v (OK)\", settings.key, entries)\n\n\t// Instantiate a new Registrar, passing in test data.\n\tregistrar := NewRegistrar(\n\t\tclient,\n\t\tservice,\n\t\tlog.With(log.NewLogfmtLogger(os.Stderr), \"component\", \"registrar\"),\n\t)\n\n\t// Register our instance.\n\tregistrar.Register()\n\tt.Log(\"Registered\")\n\n\t// Deregister our instance. (so we test registrar only scenario)\n\tregistrar.Deregister()\n\tt.Log(\"Deregistered\")\n\n}\n\nfunc within(d time.Duration, f func() bool) bool {\n\tdeadline := time.Now().Add(d)\n\tfor time.Now().Before(deadline) {\n\t\tif f() {\n\t\t\treturn true\n\t\t}\n\t\ttime.Sleep(d / 10)\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "sd/etcdv3/registrar.go",
    "content": "package etcdv3\n\nimport (\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-kit/log\"\n)\n\nconst minHeartBeatTime = 500 * time.Millisecond\n\n// Registrar registers service instance liveness information to etcd.\ntype Registrar struct {\n\tclient  Client\n\tservice Service\n\tlogger  log.Logger\n\n\tquitmtx sync.Mutex\n\tquit    chan struct{}\n}\n\n// Service holds the instance identifying data you want to publish to etcd. Key\n// must be unique, and value is the string returned to subscribers, typically\n// called the \"instance\" string in other parts of package sd.\ntype Service struct {\n\tKey   string // unique key, e.g. \"/service/foobar/1.2.3.4:8080\"\n\tValue string // returned to subscribers, e.g. \"http://1.2.3.4:8080\"\n\tTTL   *TTLOption\n}\n\n// TTLOption allow setting a key with a TTL. This option will be used by a loop\n// goroutine which regularly refreshes the lease of the key.\ntype TTLOption struct {\n\theartbeat time.Duration // e.g. time.Second * 3\n\tttl       time.Duration // e.g. time.Second * 10\n}\n\n// NewTTLOption returns a TTLOption that contains proper TTL settings. Heartbeat\n// is used to refresh the lease of the key periodically; its value should be at\n// least 500ms. TTL defines the lease of the key; its value should be\n// significantly greater than heartbeat.\n//\n// Good default values might be 3s heartbeat, 10s TTL.\nfunc NewTTLOption(heartbeat, ttl time.Duration) *TTLOption {\n\tif heartbeat <= minHeartBeatTime {\n\t\theartbeat = minHeartBeatTime\n\t}\n\tif ttl <= heartbeat {\n\t\tttl = 3 * heartbeat\n\t}\n\treturn &TTLOption{\n\t\theartbeat: heartbeat,\n\t\tttl:       ttl,\n\t}\n}\n\n// NewRegistrar returns a etcd Registrar acting on the provided catalog\n// registration (service).\nfunc NewRegistrar(client Client, service Service, logger log.Logger) *Registrar {\n\treturn &Registrar{\n\t\tclient:  client,\n\t\tservice: service,\n\t\tlogger:  log.With(logger, \"key\", service.Key, \"value\", service.Value),\n\t}\n}\n\n// Register implements the sd.Registrar interface. Call it when you want your\n// service to be registered in etcd, typically at startup.\nfunc (r *Registrar) Register() {\n\tif err := r.client.Register(r.service); err != nil {\n\t\tr.logger.Log(\"err\", err)\n\t\treturn\n\t}\n\tif r.service.TTL != nil {\n\t\tr.logger.Log(\"action\", \"register\", \"lease\", r.client.LeaseID())\n\t} else {\n\t\tr.logger.Log(\"action\", \"register\")\n\t}\n}\n\n// Deregister implements the sd.Registrar interface. Call it when you want your\n// service to be deregistered from etcd, typically just prior to shutdown.\nfunc (r *Registrar) Deregister() {\n\tif err := r.client.Deregister(r.service); err != nil {\n\t\tr.logger.Log(\"err\", err)\n\t} else {\n\t\tr.logger.Log(\"action\", \"deregister\")\n\t}\n\n\tr.quitmtx.Lock()\n\tdefer r.quitmtx.Unlock()\n\tif r.quit != nil {\n\t\tclose(r.quit)\n\t\tr.quit = nil\n\t}\n}\n"
  },
  {
    "path": "sd/etcdv3/registrar_test.go",
    "content": "package etcdv3\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/go-kit/log\"\n)\n\n// testClient is a basic implementation of Client\ntype testClient struct {\n\tregisterRes error // value returned when Register or Deregister is called\n}\n\nfunc (tc *testClient) GetEntries(prefix string) ([]string, error) {\n\treturn nil, nil\n}\n\nfunc (tc *testClient) WatchPrefix(prefix string, ch chan struct{}) {\n}\n\nfunc (tc *testClient) Register(s Service) error {\n\treturn tc.registerRes\n}\n\nfunc (tc *testClient) Deregister(s Service) error {\n\treturn tc.registerRes\n}\n\nfunc (tc *testClient) LeaseID() int64 {\n\treturn 0\n}\n\n// default service used to build registrar in our tests\nvar testService = Service{\n\tKey:   \"testKey\",\n\tValue: \"testValue\",\n\tTTL:   nil,\n}\n\n// NewRegistar should return a registar with a logger using the service key and value\nfunc TestNewRegistar(t *testing.T) {\n\tc := Client(&testClient{nil})\n\tbuf := &bytes.Buffer{}\n\tlogger := log.NewLogfmtLogger(buf)\n\tr := NewRegistrar(\n\t\tc,\n\t\ttestService,\n\t\tlogger,\n\t)\n\n\tif err := r.logger.Log(\"msg\", \"message\"); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif want, have := \"key=testKey value=testValue msg=message\\n\", buf.String(); want != have {\n\t\tt.Errorf(\"\\nwant: %shave: %s\", want, have)\n\t}\n}\n\nfunc TestRegister(t *testing.T) {\n\t// Register log the error returned by the client or log the successful registration action\n\t// table of test cases for method Register\n\tvar registerTestTable = []struct {\n\t\tregisterRes error  // value returned by the client on calls to Register\n\t\tlog         string // expected log by the registrar\n\n\t}{\n\t\t// test case: an error is returned by the client\n\t\t{errors.New(\"regError\"), \"key=testKey value=testValue err=regError\\n\"},\n\t\t// test case: registration successful\n\t\t{nil, \"key=testKey value=testValue action=register\\n\"},\n\t}\n\n\tfor _, tc := range registerTestTable {\n\t\tc := Client(&testClient{tc.registerRes})\n\t\tbuf := &bytes.Buffer{}\n\t\tlogger := log.NewLogfmtLogger(buf)\n\t\tr := NewRegistrar(\n\t\t\tc,\n\t\t\ttestService,\n\t\t\tlogger,\n\t\t)\n\t\tr.Register()\n\t\tif want, have := tc.log, buf.String(); want != have {\n\t\t\tt.Fatalf(\"want %v, have %v\", want, have)\n\t\t}\n\t}\n}\n\nfunc TestDeregister(t *testing.T) {\n\t// Deregister log the error returned by the client or log the successful deregistration action\n\t// table of test cases for method Deregister\n\tvar deregisterTestTable = []struct {\n\t\tderegisterRes error  // value returned by the client on calls to Deregister\n\t\tlog           string // expected log by the registrar\n\t}{\n\t\t// test case: an error is returned by the client\n\t\t{errors.New(\"deregError\"), \"key=testKey value=testValue err=deregError\\n\"},\n\t\t// test case: deregistration successful\n\t\t{nil, \"key=testKey value=testValue action=deregister\\n\"},\n\t}\n\n\tfor _, tc := range deregisterTestTable {\n\t\tc := Client(&testClient{tc.deregisterRes})\n\t\tbuf := &bytes.Buffer{}\n\t\tlogger := log.NewLogfmtLogger(buf)\n\t\tr := NewRegistrar(\n\t\t\tc,\n\t\t\ttestService,\n\t\t\tlogger,\n\t\t)\n\t\tr.Deregister()\n\t\tif want, have := tc.log, buf.String(); want != have {\n\t\t\tt.Fatalf(\"want %v, have %v\", want, have)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sd/eureka/doc.go",
    "content": "// Package eureka provides Instancer and Registrar implementations for Netflix OSS's Eureka\npackage eureka\n"
  },
  {
    "path": "sd/eureka/instancer.go",
    "content": "package eureka\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/hudl/fargo\"\n\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/kit/sd/internal/instance\"\n\t\"github.com/go-kit/log\"\n)\n\n// Instancer yields instances stored in the Eureka registry for the given app.\n// Changes in that app are watched and will update the subscribers.\ntype Instancer struct {\n\tcache  *instance.Cache\n\tconn   fargoConnection\n\tapp    string\n\tlogger log.Logger\n\tquitc  chan chan struct{}\n}\n\n// NewInstancer returns a Eureka Instancer. It will start watching the given\n// app string for changes, and update the subscribers accordingly.\nfunc NewInstancer(conn fargoConnection, app string, logger log.Logger) *Instancer {\n\tlogger = log.With(logger, \"app\", app)\n\n\ts := &Instancer{\n\t\tcache:  instance.NewCache(),\n\t\tconn:   conn,\n\t\tapp:    app,\n\t\tlogger: logger,\n\t\tquitc:  make(chan chan struct{}),\n\t}\n\n\tdone := make(chan struct{})\n\tupdates := conn.ScheduleAppUpdates(app, true, done)\n\ts.consume(<-updates)\n\tgo s.loop(updates, done)\n\treturn s\n}\n\n// Stop terminates the Instancer.\nfunc (s *Instancer) Stop() {\n\tq := make(chan struct{})\n\ts.quitc <- q\n\t<-q\n\ts.quitc = nil\n}\n\nfunc (s *Instancer) consume(update fargo.AppUpdate) {\n\tif update.Err != nil {\n\t\ts.logger.Log(\"during\", \"Update\", \"err\", update.Err)\n\t\ts.cache.Update(sd.Event{Err: update.Err})\n\t\treturn\n\t}\n\tinstances := convertFargoAppToInstances(update.App)\n\ts.logger.Log(\"instances\", len(instances))\n\ts.cache.Update(sd.Event{Instances: instances})\n}\n\nfunc (s *Instancer) loop(updates <-chan fargo.AppUpdate, done chan<- struct{}) {\n\tdefer close(done)\n\n\tfor {\n\t\tselect {\n\t\tcase update := <-updates:\n\t\t\ts.consume(update)\n\t\tcase q := <-s.quitc:\n\t\t\tclose(q)\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (s *Instancer) getInstances() ([]string, error) {\n\tapp, err := s.conn.GetApp(s.app)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn convertFargoAppToInstances(app), nil\n}\n\nfunc convertFargoAppToInstances(app *fargo.Application) []string {\n\tinstances := make([]string, len(app.Instances))\n\tfor i, inst := range app.Instances {\n\t\tinstances[i] = fmt.Sprintf(\"%s:%d\", inst.IPAddr, inst.Port)\n\t}\n\treturn instances\n}\n\n// Register implements Instancer.\nfunc (s *Instancer) Register(ch chan<- sd.Event) {\n\ts.cache.Register(ch)\n}\n\n// Deregister implements Instancer.\nfunc (s *Instancer) Deregister(ch chan<- sd.Event) {\n\ts.cache.Deregister(ch)\n}\n\n// state returns the current state of instance.Cache, only for testing\nfunc (s *Instancer) state() sd.Event {\n\treturn s.cache.State()\n}\n"
  },
  {
    "path": "sd/eureka/instancer_test.go",
    "content": "package eureka\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/hudl/fargo\"\n\n\t\"github.com/go-kit/kit/sd\"\n)\n\nvar _ sd.Instancer = (*Instancer)(nil) // API check\n\nfunc TestInstancer(t *testing.T) {\n\tconnection := &testConnection{\n\t\tinstances:      []*fargo.Instance{instanceTest1, instanceTest2},\n\t\terrApplication: nil,\n\t}\n\n\tinstancer := NewInstancer(connection, appNameTest, loggerTest)\n\tdefer instancer.Stop()\n\n\tstate := instancer.state()\n\tif state.Err != nil {\n\t\tt.Fatal(state.Err)\n\t}\n\n\tif want, have := 2, len(state.Instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\nfunc TestInstancerReceivesUpdates(t *testing.T) {\n\tconnection := &testConnection{\n\t\tinstances:      []*fargo.Instance{instanceTest1},\n\t\terrApplication: nil,\n\t}\n\n\tinstancer := NewInstancer(connection, appNameTest, loggerTest)\n\tdefer instancer.Stop()\n\n\tverifyCount := func(want int) (have int, converged bool) {\n\t\tconst maxPollAttempts = 5\n\t\tconst delayPerAttempt = 200 * time.Millisecond\n\t\tfor i := 1; ; i++ {\n\t\t\tstate := instancer.state()\n\t\t\tif have := len(state.Instances); want == have {\n\t\t\t\treturn have, true\n\t\t\t} else if i == maxPollAttempts {\n\t\t\t\treturn have, false\n\t\t\t}\n\t\t\ttime.Sleep(delayPerAttempt)\n\t\t}\n\t}\n\n\tif have, converged := verifyCount(1); !converged {\n\t\tt.Fatalf(\"initial: want %d, have %d\", 1, have)\n\t}\n\n\tif err := connection.RegisterInstance(instanceTest2); err != nil {\n\t\tt.Fatalf(\"failed to register an instance: %v\", err)\n\t}\n\tif have, converged := verifyCount(2); !converged {\n\t\tt.Fatalf(\"after registration: want %d, have %d\", 2, have)\n\t}\n\n\tif err := connection.DeregisterInstance(instanceTest1); err != nil {\n\t\tt.Fatalf(\"failed to unregister an instance: %v\", err)\n\t}\n\tif have, converged := verifyCount(1); !converged {\n\t\tt.Fatalf(\"after deregistration: want %d, have %d\", 1, have)\n\t}\n}\n\nfunc TestBadInstancerScheduleUpdates(t *testing.T) {\n\tconnection := &testConnection{\n\t\tinstances:      []*fargo.Instance{instanceTest1},\n\t\terrApplication: errTest,\n\t}\n\n\tinstancer := NewInstancer(connection, appNameTest, loggerTest)\n\tdefer instancer.Stop()\n\n\tstate := instancer.state()\n\tif state.Err == nil {\n\t\tt.Fatal(\"expecting error\")\n\t}\n\n\tif want, have := 0, len(state.Instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n"
  },
  {
    "path": "sd/eureka/integration_test.go",
    "content": "//go:build integration\n// +build integration\n\npackage eureka\n\nimport (\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/hudl/fargo\"\n\n\t\"github.com/go-kit/log\"\n)\n\n// Package sd/eureka provides a wrapper around the Netflix Eureka service\n// registry by way of the Fargo library. This test assumes the user has an\n// instance of Eureka available at the address in the environment variable.\n// Example `${EUREKA_ADDR}` format: http://localhost:8761/eureka\n//\n// NOTE: when starting a Eureka server for integration testing, ensure\n// the response cache interval is reduced to one second. This can be\n// achieved with the following Java argument:\n// `-Deureka.server.responseCacheUpdateIntervalMs=1000`\nfunc TestIntegration(t *testing.T) {\n\teurekaAddr := os.Getenv(\"EUREKA_ADDR\")\n\tif eurekaAddr == \"\" {\n\t\tt.Skip(\"EUREKA_ADDR is not set\")\n\t}\n\n\tlogger := log.NewLogfmtLogger(os.Stderr)\n\tlogger = log.With(logger, \"ts\", log.DefaultTimestamp)\n\n\tvar fargoConfig fargo.Config\n\t// Target Eureka server(s).\n\tfargoConfig.Eureka.ServiceUrls = []string{eurekaAddr}\n\t// How often the subscriber should poll for updates.\n\tfargoConfig.Eureka.PollIntervalSeconds = 1\n\n\t// Create a Fargo connection and a Eureka registrar.\n\tfargoConnection := fargo.NewConnFromConfig(fargoConfig)\n\tregistrar1 := NewRegistrar(&fargoConnection, instanceTest1, log.With(logger, \"component\", \"registrar1\"))\n\n\t// Register one instance.\n\tregistrar1.Register()\n\tdefer registrar1.Deregister()\n\n\t// Build a Eureka instancer.\n\tinstancer := NewInstancer(\n\t\t&fargoConnection,\n\t\tappNameTest,\n\t\tlog.With(logger, \"component\", \"instancer\"),\n\t)\n\tdefer instancer.Stop()\n\n\t// checks every 100ms (fr up to 10s) for the expected number of instances to be reported\n\twaitForInstances := func(count int) {\n\t\tfor t := 0; t < 100; t++ {\n\t\t\tstate := instancer.state()\n\t\t\tif len(state.Instances) == count {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttime.Sleep(100 * time.Millisecond)\n\t\t}\n\t\tstate := instancer.state()\n\t\tif state.Err != nil {\n\t\t\tt.Error(state.Err)\n\t\t}\n\t\tif want, have := 1, len(state.Instances); want != have {\n\t\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t\t}\n\t}\n\n\t// We should have one instance immediately after subscriber instantiation.\n\twaitForInstances(1)\n\n\t// Register a second instance\n\tregistrar2 := NewRegistrar(&fargoConnection, instanceTest2, log.With(logger, \"component\", \"registrar2\"))\n\tregistrar2.Register()\n\tdefer registrar2.Deregister() // In case of exceptional circumstances.\n\n\t// This should be enough time for a scheduled update assuming Eureka is\n\t// configured with the properties mentioned in the function comments.\n\twaitForInstances(2)\n\n\t// Deregister the second instance.\n\tregistrar2.Deregister()\n\n\t// Wait for another scheduled update.\n\t// And then there was one.\n\twaitForInstances(1)\n}\n"
  },
  {
    "path": "sd/eureka/registrar.go",
    "content": "package eureka\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/hudl/fargo\"\n\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/log\"\n)\n\n// Matches official Netflix Java client default.\nconst defaultRenewalInterval = 30 * time.Second\n\n// The methods of fargo.Connection used in this package.\ntype fargoConnection interface {\n\tRegisterInstance(instance *fargo.Instance) error\n\tDeregisterInstance(instance *fargo.Instance) error\n\tReregisterInstance(instance *fargo.Instance) error\n\tHeartBeatInstance(instance *fargo.Instance) error\n\tScheduleAppUpdates(name string, await bool, done <-chan struct{}) <-chan fargo.AppUpdate\n\tGetApp(name string) (*fargo.Application, error)\n}\n\ntype fargoUnsuccessfulHTTPResponse struct {\n\tstatusCode    int\n\tmessagePrefix string\n}\n\nfunc (u *fargoUnsuccessfulHTTPResponse) Error() string {\n\treturn fmt.Sprintf(\"err=%s code=%d\", u.messagePrefix, u.statusCode)\n}\n\n// Registrar maintains service instance liveness information in Eureka.\ntype Registrar struct {\n\tconn     fargoConnection\n\tinstance *fargo.Instance\n\tlogger   log.Logger\n\tquitc    chan chan struct{}\n\tsync.Mutex\n}\n\nvar _ sd.Registrar = (*Registrar)(nil)\n\n// NewRegistrar returns an Eureka Registrar acting on behalf of the provided\n// Fargo connection and instance. See the integration test for usage examples.\nfunc NewRegistrar(conn fargoConnection, instance *fargo.Instance, logger log.Logger) *Registrar {\n\treturn &Registrar{\n\t\tconn:     conn,\n\t\tinstance: instance,\n\t\tlogger:   log.With(logger, \"service\", instance.App, \"address\", fmt.Sprintf(\"%s:%d\", instance.IPAddr, instance.Port)),\n\t}\n}\n\n// Register implements sd.Registrar.\nfunc (r *Registrar) Register() {\n\tr.Lock()\n\tdefer r.Unlock()\n\n\tif r.quitc != nil {\n\t\treturn // Already in the registration loop.\n\t}\n\n\tif err := r.conn.RegisterInstance(r.instance); err != nil {\n\t\tr.logger.Log(\"during\", \"Register\", \"err\", err)\n\t}\n\n\tr.quitc = make(chan chan struct{})\n\tgo r.loop()\n}\n\n// Deregister implements sd.Registrar.\nfunc (r *Registrar) Deregister() {\n\tr.Lock()\n\tdefer r.Unlock()\n\n\tif r.quitc == nil {\n\t\treturn // Already deregistered.\n\t}\n\n\tq := make(chan struct{})\n\tr.quitc <- q\n\t<-q\n\tr.quitc = nil\n}\n\nfunc (r *Registrar) loop() {\n\tvar renewalInterval time.Duration\n\tif r.instance.LeaseInfo.RenewalIntervalInSecs > 0 {\n\t\trenewalInterval = time.Duration(r.instance.LeaseInfo.RenewalIntervalInSecs) * time.Second\n\t} else {\n\t\trenewalInterval = defaultRenewalInterval\n\t}\n\tticker := time.NewTicker(renewalInterval)\n\tdefer ticker.Stop()\n\n\tfor {\n\t\tselect {\n\t\tcase <-ticker.C:\n\t\t\tif err := r.heartbeat(); err != nil {\n\t\t\t\tr.logger.Log(\"during\", \"heartbeat\", \"err\", err)\n\t\t\t}\n\n\t\tcase q := <-r.quitc:\n\t\t\tif err := r.conn.DeregisterInstance(r.instance); err != nil {\n\t\t\t\tr.logger.Log(\"during\", \"Deregister\", \"err\", err)\n\t\t\t}\n\t\t\tclose(q)\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc httpResponseStatusCode(err error) (code int, present bool) {\n\tif code, ok := fargo.HTTPResponseStatusCode(err); ok {\n\t\treturn code, true\n\t}\n\t// Allow injection of errors for testing.\n\tif u, ok := err.(*fargoUnsuccessfulHTTPResponse); ok {\n\t\treturn u.statusCode, true\n\t}\n\treturn 0, false\n}\n\nfunc isNotFound(err error) bool {\n\tcode, ok := httpResponseStatusCode(err)\n\treturn ok && code == http.StatusNotFound\n}\n\nfunc (r *Registrar) heartbeat() error {\n\terr := r.conn.HeartBeatInstance(r.instance)\n\tif err == nil {\n\t\treturn nil\n\t}\n\tif isNotFound(err) {\n\t\t// Instance expired (e.g. network partition). Re-register.\n\t\treturn r.conn.ReregisterInstance(r.instance)\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "sd/eureka/registrar_test.go",
    "content": "package eureka\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestRegistrar(t *testing.T) {\n\tconnection := &testConnection{\n\t\terrHeartbeat: errTest,\n\t}\n\n\tregistrar1 := NewRegistrar(connection, instanceTest1, loggerTest)\n\tregistrar2 := NewRegistrar(connection, instanceTest2, loggerTest)\n\n\t// Not registered.\n\tregistrar1.Deregister()\n\tif want, have := 0, len(connection.instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\t// Register.\n\tregistrar1.Register()\n\tif want, have := 1, len(connection.instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\tregistrar2.Register()\n\tif want, have := 2, len(connection.instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\t// Deregister.\n\tregistrar1.Deregister()\n\tif want, have := 1, len(connection.instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\t// Already registered.\n\tregistrar1.Register()\n\tif want, have := 2, len(connection.instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\tregistrar1.Register()\n\tif want, have := 2, len(connection.instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\t// Wait for a heartbeat failure.\n\ttime.Sleep(1010 * time.Millisecond)\n\tif want, have := 2, len(connection.instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\tregistrar1.Deregister()\n\tif want, have := 1, len(connection.instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\nfunc TestBadRegister(t *testing.T) {\n\tconnection := &testConnection{\n\t\terrRegister: errTest,\n\t}\n\n\tregistrar := NewRegistrar(connection, instanceTest1, loggerTest)\n\tregistrar.Register()\n\tif want, have := 0, len(connection.instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\nfunc TestBadDeregister(t *testing.T) {\n\tconnection := &testConnection{\n\t\terrDeregister: errTest,\n\t}\n\n\tregistrar := NewRegistrar(connection, instanceTest1, loggerTest)\n\tregistrar.Register()\n\tif want, have := 1, len(connection.instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\tregistrar.Deregister()\n\tif want, have := 1, len(connection.instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\nfunc TestExpiredInstance(t *testing.T) {\n\tconnection := &testConnection{\n\t\terrHeartbeat: errNotFound,\n\t}\n\n\tregistrar := NewRegistrar(connection, instanceTest1, loggerTest)\n\tregistrar.Register()\n\n\t// Wait for a heartbeat failure.\n\ttime.Sleep(1010 * time.Millisecond)\n\n\tif want, have := 1, len(connection.instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n"
  },
  {
    "path": "sd/eureka/util_test.go",
    "content": "package eureka\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-kit/log\"\n\t\"github.com/hudl/fargo\"\n)\n\ntype testConnection struct {\n\tmu        sync.RWMutex\n\tinstances []*fargo.Instance\n\n\terrApplication error\n\terrHeartbeat   error\n\terrRegister    error\n\terrDeregister  error\n}\n\nvar (\n\terrTest       = errors.New(\"kaboom\")\n\terrNotFound   = &fargoUnsuccessfulHTTPResponse{statusCode: 404, messagePrefix: \"not found\"}\n\tloggerTest    = log.NewNopLogger()\n\tappNameTest   = \"go-kit\"\n\tinstanceTest1 = &fargo.Instance{\n\t\tHostName:         \"serveregistrar1.acme.org\",\n\t\tPort:             8080,\n\t\tApp:              appNameTest,\n\t\tIPAddr:           \"192.168.0.1\",\n\t\tVipAddress:       \"192.168.0.1\",\n\t\tSecureVipAddress: \"192.168.0.1\",\n\t\tHealthCheckUrl:   \"http://serveregistrar1.acme.org:8080/healthz\",\n\t\tStatusPageUrl:    \"http://serveregistrar1.acme.org:8080/status\",\n\t\tHomePageUrl:      \"http://serveregistrar1.acme.org:8080/\",\n\t\tStatus:           fargo.UP,\n\t\tDataCenterInfo:   fargo.DataCenterInfo{Name: fargo.MyOwn},\n\t\tLeaseInfo:        fargo.LeaseInfo{RenewalIntervalInSecs: 1},\n\t}\n\tinstanceTest2 = &fargo.Instance{\n\t\tHostName:         \"serveregistrar2.acme.org\",\n\t\tPort:             8080,\n\t\tApp:              appNameTest,\n\t\tIPAddr:           \"192.168.0.2\",\n\t\tVipAddress:       \"192.168.0.2\",\n\t\tSecureVipAddress: \"192.168.0.2\",\n\t\tHealthCheckUrl:   \"http://serveregistrar2.acme.org:8080/healthz\",\n\t\tStatusPageUrl:    \"http://serveregistrar2.acme.org:8080/status\",\n\t\tHomePageUrl:      \"http://serveregistrar2.acme.org:8080/\",\n\t\tStatus:           fargo.UP,\n\t\tDataCenterInfo:   fargo.DataCenterInfo{Name: fargo.MyOwn},\n\t}\n)\n\nvar _ fargoConnection = (*testConnection)(nil)\n\nfunc (c *testConnection) RegisterInstance(i *fargo.Instance) error {\n\tif c.errRegister != nil {\n\t\treturn c.errRegister\n\t}\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tfor _, instance := range c.instances {\n\t\tif reflect.DeepEqual(*instance, *i) {\n\t\t\treturn errors.New(\"already registered\")\n\t\t}\n\t}\n\tc.instances = append(c.instances, i)\n\treturn nil\n}\n\nfunc (c *testConnection) HeartBeatInstance(i *fargo.Instance) error {\n\treturn c.errHeartbeat\n}\n\nfunc (c *testConnection) DeregisterInstance(i *fargo.Instance) error {\n\tif c.errDeregister != nil {\n\t\treturn c.errDeregister\n\t}\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tremaining := make([]*fargo.Instance, 0, len(c.instances))\n\tfor _, instance := range c.instances {\n\t\tif reflect.DeepEqual(*instance, *i) {\n\t\t\tcontinue\n\t\t}\n\t\tremaining = append(remaining, instance)\n\t}\n\tif len(remaining) == len(c.instances) {\n\t\treturn errors.New(\"not registered\")\n\t}\n\tc.instances = remaining\n\treturn nil\n}\n\nfunc (c *testConnection) ReregisterInstance(ins *fargo.Instance) error {\n\treturn nil\n}\n\nfunc (c *testConnection) instancesForApplication(name string) []*fargo.Instance {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\tinstances := make([]*fargo.Instance, 0, len(c.instances))\n\tfor _, i := range c.instances {\n\t\tif i.App == name {\n\t\t\tinstances = append(instances, i)\n\t\t}\n\t}\n\treturn instances\n}\n\nfunc (c *testConnection) GetApp(name string) (*fargo.Application, error) {\n\tif err := c.errApplication; err != nil {\n\t\treturn nil, err\n\t}\n\tinstances := c.instancesForApplication(name)\n\tif len(instances) == 0 {\n\t\treturn nil, fmt.Errorf(\"application not found for name=%s\", name)\n\t}\n\treturn &fargo.Application{Name: name, Instances: instances}, nil\n}\n\nfunc (c *testConnection) ScheduleAppUpdates(name string, await bool, done <-chan struct{}) <-chan fargo.AppUpdate {\n\tupdatec := make(chan fargo.AppUpdate, 1)\n\tsend := func() {\n\t\tapp, err := c.GetApp(name)\n\t\tselect {\n\t\tcase updatec <- fargo.AppUpdate{App: app, Err: err}:\n\t\tdefault:\n\t\t}\n\t}\n\n\tif await {\n\t\tsend()\n\t}\n\tgo func() {\n\t\tticker := time.NewTicker(100 * time.Millisecond)\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase <-ticker.C:\n\t\t\t\tsend()\n\t\t\tcase <-done:\n\t\t\t\tticker.Stop()\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\treturn updatec\n}\n"
  },
  {
    "path": "sd/factory.go",
    "content": "package sd\n\nimport (\n\t\"io\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n)\n\n// Factory is a function that converts an instance string (e.g. host:port) to a\n// specific endpoint. Instances that provide multiple endpoints require multiple\n// factories. A factory also returns an io.Closer that's invoked when the\n// instance goes away and needs to be cleaned up. Factories may return nil\n// closers.\n//\n// Users are expected to provide their own factory functions that assume\n// specific transports, or can deduce transports by parsing the instance string.\ntype Factory func(instance string) (endpoint.Endpoint, io.Closer, error)\n"
  },
  {
    "path": "sd/instancer.go",
    "content": "package sd\n\n// Event represents a push notification generated from the underlying service discovery\n// implementation. It contains either a full set of available resource instances, or\n// an error indicating some issue with obtaining information from discovery backend.\n// Examples of errors may include loosing connection to the discovery backend, or\n// trying to look up resource instances using an incorrectly formatted key.\n// After receiving an Event with an error the listenter should treat previously discovered\n// resource instances as stale (although it may choose to continue using them).\n// If the Instancer is able to restore connection to the discovery backend it must push\n// another Event with the current set of resource instances.\ntype Event struct {\n\tInstances []string\n\tErr       error\n}\n\n// Instancer listens to a service discovery system and notifies registered\n// observers of changes in the resource instances. Every event sent to the channels\n// contains a complete set of instances known to the Instancer. That complete set is\n// sent immediately upon registering the channel, and on any future updates from\n// discovery system.\ntype Instancer interface {\n\tRegister(chan<- Event)\n\tDeregister(chan<- Event)\n\tStop()\n}\n\n// FixedInstancer yields a fixed set of instances.\ntype FixedInstancer []string\n\n// Register implements Instancer.\nfunc (d FixedInstancer) Register(ch chan<- Event) { ch <- Event{Instances: d} }\n\n// Deregister implements Instancer.\nfunc (d FixedInstancer) Deregister(ch chan<- Event) {}\n\n// Stop implements Instancer.\nfunc (d FixedInstancer) Stop() {}\n"
  },
  {
    "path": "sd/internal/instance/cache.go",
    "content": "package instance\n\nimport (\n\t\"reflect\"\n\t\"sort\"\n\t\"sync\"\n\n\t\"github.com/go-kit/kit/sd\"\n)\n\n// Cache keeps track of resource instances provided to it via Update method\n// and implements the Instancer interface\ntype Cache struct {\n\tmtx   sync.RWMutex\n\tstate sd.Event\n\treg   registry\n}\n\n// NewCache creates a new Cache.\nfunc NewCache() *Cache {\n\treturn &Cache{\n\t\treg: registry{},\n\t}\n}\n\n// Update receives new instances from service discovery, stores them internally,\n// and notifies all registered listeners.\nfunc (c *Cache) Update(event sd.Event) {\n\tc.mtx.Lock()\n\tdefer c.mtx.Unlock()\n\n\tsort.Strings(event.Instances)\n\tif reflect.DeepEqual(c.state, event) {\n\t\treturn // no need to broadcast the same instances\n\t}\n\n\tc.state = event\n\tc.reg.broadcast(event)\n}\n\n// State returns the current state of discovery (instances or error) as sd.Event\nfunc (c *Cache) State() sd.Event {\n\tc.mtx.RLock()\n\tevent := c.state\n\tc.mtx.RUnlock()\n\teventCopy := copyEvent(event)\n\treturn eventCopy\n}\n\n// Stop implements Instancer. Since the cache is just a plain-old store of data,\n// Stop is a no-op.\nfunc (c *Cache) Stop() {}\n\n// Register implements Instancer.\nfunc (c *Cache) Register(ch chan<- sd.Event) {\n\tc.mtx.Lock()\n\tdefer c.mtx.Unlock()\n\tc.reg.register(ch)\n\tevent := c.state\n\teventCopy := copyEvent(event)\n\t// always push the current state to new channels\n\tch <- eventCopy\n}\n\n// Deregister implements Instancer.\nfunc (c *Cache) Deregister(ch chan<- sd.Event) {\n\tc.mtx.Lock()\n\tdefer c.mtx.Unlock()\n\tc.reg.deregister(ch)\n}\n\n// registry is not goroutine-safe.\ntype registry map[chan<- sd.Event]struct{}\n\nfunc (r registry) broadcast(event sd.Event) {\n\tfor c := range r {\n\t\teventCopy := copyEvent(event)\n\t\tc <- eventCopy\n\t}\n}\n\nfunc (r registry) register(c chan<- sd.Event) {\n\tr[c] = struct{}{}\n}\n\nfunc (r registry) deregister(c chan<- sd.Event) {\n\tdelete(r, c)\n}\n\n// copyEvent does a deep copy on sd.Event\nfunc copyEvent(e sd.Event) sd.Event {\n\t// observers all need their own copy of event\n\t// because they can directly modify event.Instances\n\t// for example, by calling sort.Strings\n\tif e.Instances == nil {\n\t\treturn e\n\t}\n\tinstances := make([]string, len(e.Instances))\n\tcopy(instances, e.Instances)\n\te.Instances = instances\n\treturn e\n}\n"
  },
  {
    "path": "sd/internal/instance/cache_test.go",
    "content": "package instance\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/log\"\n)\n\nvar _ sd.Instancer = (*Cache)(nil) // API check\n\n// The test verifies the following:\n//   registering causes initial notification of the current state\n//   instances are sorted\n//   different update causes new notification\n//   identical notifications cause no updates\n//   no updates after de-registering\nfunc TestCache(t *testing.T) {\n\te1 := sd.Event{Instances: []string{\"y\", \"x\"}} // not sorted\n\te2 := sd.Event{Instances: []string{\"c\", \"a\", \"b\"}}\n\n\tcache := NewCache()\n\tif want, have := 0, len(cache.State().Instances); want != have {\n\t\tt.Fatalf(\"want %v instances, have %v\", want, have)\n\t}\n\n\tcache.Update(e1) // sets initial state\n\tif want, have := 2, len(cache.State().Instances); want != have {\n\t\tt.Fatalf(\"want %v instances, have %v\", want, have)\n\t}\n\n\tr1 := make(chan sd.Event)\n\tgo cache.Register(r1)\n\texpectUpdate(t, r1, []string{\"x\", \"y\"})\n\n\tgo cache.Update(e2) // different set\n\texpectUpdate(t, r1, []string{\"a\", \"b\", \"c\"})\n\n\tcache.Deregister(r1)\n\tclose(r1)\n}\n\nfunc expectUpdate(t *testing.T, r chan sd.Event, expect []string) {\n\tselect {\n\tcase e := <-r:\n\t\tif want, have := expect, e.Instances; !reflect.DeepEqual(want, have) {\n\t\t\tt.Fatalf(\"want: %v, have: %v\", want, have)\n\t\t}\n\tcase <-time.After(time.Second):\n\t\tt.Fatalf(\"did not receive expected update %v\", expect)\n\t}\n}\n\nfunc TestRegistry(t *testing.T) {\n\treg := make(registry)\n\tc1 := make(chan sd.Event, 1)\n\tc2 := make(chan sd.Event, 1)\n\treg.register(c1)\n\treg.register(c2)\n\n\t// validate that both channels receive the update\n\treg.broadcast(sd.Event{Instances: []string{\"x\", \"y\"}})\n\tif want, have := []string{\"x\", \"y\"}, (<-c1).Instances; !reflect.DeepEqual(want, have) {\n\t\tt.Fatalf(\"want: %v, have: %v\", want, have)\n\t}\n\tif want, have := []string{\"x\", \"y\"}, (<-c2).Instances; !reflect.DeepEqual(want, have) {\n\t\tt.Fatalf(\"want: %v, have: %v\", want, have)\n\t}\n\n\treg.deregister(c1)\n\treg.deregister(c2)\n\tclose(c1)\n\tclose(c2)\n\t// if deregister didn't work, broadcast would panic on closed channels\n\treg.broadcast(sd.Event{Instances: []string{\"x\", \"y\"}})\n}\n\n// This test is meant to be run with the race detector enabled: -race.\n// It ensures that every registered observer receives a copy\n// of sd.Event.Instances because observers can directly modify the field.\n// For example, endpointCache calls sort.Strings() on sd.Event.Instances.\nfunc TestDataRace(t *testing.T) {\n\tinstances := make([]string, 0)\n\t// the number of iterations here maters because we need sort.Strings to\n\t// perform a Swap in doPivot -> medianOfThree to cause a data race.\n\tfor i := 1; i < 1000; i++ {\n\t\tinstances = append(instances, fmt.Sprintf(\"%v\", i))\n\t}\n\te1 := sd.Event{Instances: instances}\n\tcache := NewCache()\n\tcache.Update(e1)\n\tnullEndpoint := func(_ context.Context, _ interface{}) (interface{}, error) {\n\t\treturn nil, nil\n\t}\n\tnullFactory := func(instance string) (endpoint.Endpoint, io.Closer, error) {\n\t\treturn nullEndpoint, nil, nil\n\t}\n\tlogger := log.Logger(log.LoggerFunc(func(keyvals ...interface{}) error {\n\t\treturn nil\n\t}))\n\n\tsd.NewEndpointer(cache, nullFactory, logger)\n\tsd.NewEndpointer(cache, nullFactory, logger)\n}\n"
  },
  {
    "path": "sd/lb/balancer.go",
    "content": "package lb\n\nimport (\n\t\"errors\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n)\n\n// Balancer yields endpoints according to some heuristic.\ntype Balancer interface {\n\tEndpoint() (endpoint.Endpoint, error)\n}\n\n// ErrNoEndpoints is returned when no qualifying endpoints are available.\nvar ErrNoEndpoints = errors.New(\"no endpoints available\")\n"
  },
  {
    "path": "sd/lb/doc.go",
    "content": "// Package lb implements the client-side load balancer pattern. When combined\n// with a service discovery system of record, it enables a more decentralized\n// architecture, removing the need for separate load balancers like HAProxy.\npackage lb\n"
  },
  {
    "path": "sd/lb/random.go",
    "content": "package lb\n\nimport (\n\t\"math/rand\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/sd\"\n)\n\n// NewRandom returns a load balancer that selects services randomly.\nfunc NewRandom(s sd.Endpointer, seed int64) Balancer {\n\treturn &random{\n\t\ts: s,\n\t\tr: rand.New(rand.NewSource(seed)),\n\t}\n}\n\ntype random struct {\n\ts sd.Endpointer\n\tr *rand.Rand\n}\n\nfunc (r *random) Endpoint() (endpoint.Endpoint, error) {\n\tendpoints, err := r.s.Endpoints()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(endpoints) <= 0 {\n\t\treturn nil, ErrNoEndpoints\n\t}\n\treturn endpoints[r.r.Intn(len(endpoints))], nil\n}\n"
  },
  {
    "path": "sd/lb/random_test.go",
    "content": "package lb\n\nimport (\n\t\"context\"\n\t\"math\"\n\t\"testing\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/sd\"\n)\n\nfunc TestRandom(t *testing.T) {\n\tvar (\n\t\tn          = 7\n\t\tendpoints  = make([]endpoint.Endpoint, n)\n\t\tcounts     = make([]int, n)\n\t\tseed       = int64(12345)\n\t\titerations = 1000000\n\t\twant       = iterations / n\n\t\ttolerance  = want / 100 // 1%\n\t)\n\n\tfor i := 0; i < n; i++ {\n\t\ti0 := i\n\t\tendpoints[i] = func(context.Context, interface{}) (interface{}, error) { counts[i0]++; return struct{}{}, nil }\n\t}\n\n\tendpointer := sd.FixedEndpointer(endpoints)\n\tbalancer := NewRandom(endpointer, seed)\n\n\tfor i := 0; i < iterations; i++ {\n\t\tendpoint, _ := balancer.Endpoint()\n\t\tendpoint(context.Background(), struct{}{})\n\t}\n\n\tfor i, have := range counts {\n\t\tdelta := int(math.Abs(float64(want - have)))\n\t\tif delta > tolerance {\n\t\t\tt.Errorf(\"%d: want %d, have %d, delta %d > %d tolerance\", i, want, have, delta, tolerance)\n\t\t}\n\t}\n}\n\nfunc TestRandomNoEndpoints(t *testing.T) {\n\tendpointer := sd.FixedEndpointer{}\n\tbalancer := NewRandom(endpointer, 1415926)\n\t_, err := balancer.Endpoint()\n\tif want, have := ErrNoEndpoints, err; want != have {\n\t\tt.Errorf(\"want %v, have %v\", want, have)\n\t}\n\n}\n"
  },
  {
    "path": "sd/lb/retry.go",
    "content": "package lb\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n)\n\n// RetryError is an error wrapper that is used by the retry mechanism. All\n// errors returned by the retry mechanism via its endpoint will be RetryErrors.\ntype RetryError struct {\n\tRawErrors []error // all errors encountered from endpoints directly\n\tFinal     error   // the final, terminating error\n}\n\nfunc (e RetryError) Error() string {\n\tvar suffix string\n\tif len(e.RawErrors) > 1 {\n\t\ta := make([]string, len(e.RawErrors)-1)\n\t\tfor i := 0; i < len(e.RawErrors)-1; i++ { // last one is Final\n\t\t\ta[i] = e.RawErrors[i].Error()\n\t\t}\n\t\tsuffix = fmt.Sprintf(\" (previously: %s)\", strings.Join(a, \"; \"))\n\t}\n\treturn fmt.Sprintf(\"%v%s\", e.Final, suffix)\n}\n\n// Callback is a function that is given the current attempt count and the error\n// received from the underlying endpoint. It should return whether the Retry\n// function should continue trying to get a working endpoint, and a custom error\n// if desired. The error message may be nil, but a true/false is always\n// expected. In all cases, if the replacement error is supplied, the received\n// error will be replaced in the calling context.\ntype Callback func(n int, received error) (keepTrying bool, replacement error)\n\n// Retry wraps a service load balancer and returns an endpoint oriented load\n// balancer for the specified service method. Requests to the endpoint will be\n// automatically load balanced via the load balancer. Requests that return\n// errors will be retried until they succeed, up to max times, or until the\n// timeout is elapsed, whichever comes first.\nfunc Retry(max int, timeout time.Duration, b Balancer) endpoint.Endpoint {\n\treturn RetryWithCallback(timeout, b, maxRetries(max))\n}\n\nfunc maxRetries(max int) Callback {\n\treturn func(n int, err error) (keepTrying bool, replacement error) {\n\t\treturn n < max, nil\n\t}\n}\n\nfunc alwaysRetry(int, error) (keepTrying bool, replacement error) {\n\treturn true, nil\n}\n\n// RetryWithCallback wraps a service load balancer and returns an endpoint\n// oriented load balancer for the specified service method. Requests to the\n// endpoint will be automatically load balanced via the load balancer. Requests\n// that return errors will be retried until they succeed, up to max times, until\n// the callback returns false, or until the timeout is elapsed, whichever comes\n// first.\nfunc RetryWithCallback(timeout time.Duration, b Balancer, cb Callback) endpoint.Endpoint {\n\tif cb == nil {\n\t\tcb = alwaysRetry\n\t}\n\tif b == nil {\n\t\tpanic(\"nil Balancer\")\n\t}\n\n\treturn func(ctx context.Context, request interface{}) (response interface{}, err error) {\n\t\tvar (\n\t\t\tnewctx, cancel = context.WithTimeout(ctx, timeout)\n\t\t\tresponses      = make(chan interface{}, 1)\n\t\t\terrs           = make(chan error, 1)\n\t\t\tfinal          RetryError\n\t\t)\n\t\tdefer cancel()\n\n\t\tfor i := 1; ; i++ {\n\t\t\tgo func() {\n\t\t\t\te, err := b.Endpoint()\n\t\t\t\tif err != nil {\n\t\t\t\t\terrs <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tresponse, err := e(newctx, request)\n\t\t\t\tif err != nil {\n\t\t\t\t\terrs <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tresponses <- response\n\t\t\t}()\n\n\t\t\tselect {\n\t\t\tcase <-newctx.Done():\n\t\t\t\treturn nil, newctx.Err()\n\n\t\t\tcase response := <-responses:\n\t\t\t\treturn response, nil\n\n\t\t\tcase err := <-errs:\n\t\t\t\tfinal.RawErrors = append(final.RawErrors, err)\n\t\t\t\tkeepTrying, replacement := cb(i, err)\n\t\t\t\tif replacement != nil {\n\t\t\t\t\terr = replacement\n\t\t\t\t}\n\t\t\t\tif !keepTrying {\n\t\t\t\t\tfinal.Final = err\n\t\t\t\t\treturn nil, final\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sd/lb/retry_test.go",
    "content": "package lb_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/kit/sd/lb\"\n)\n\nfunc TestRetryMaxTotalFail(t *testing.T) {\n\tvar (\n\t\tendpoints = sd.FixedEndpointer{} // no endpoints\n\t\trr        = lb.NewRoundRobin(endpoints)\n\t\tretry     = lb.Retry(999, time.Second, rr) // lots of retries\n\t\tctx       = context.Background()\n\t)\n\tif _, err := retry(ctx, struct{}{}); err == nil {\n\t\tt.Errorf(\"expected error, got none\") // should fail\n\t}\n}\n\nfunc TestRetryMaxPartialFail(t *testing.T) {\n\tvar (\n\t\tendpoints = []endpoint.Endpoint{\n\t\t\tfunc(context.Context, interface{}) (interface{}, error) { return nil, errors.New(\"error one\") },\n\t\t\tfunc(context.Context, interface{}) (interface{}, error) { return nil, errors.New(\"error two\") },\n\t\t\tfunc(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil /* OK */ },\n\t\t}\n\t\tendpointer = sd.FixedEndpointer{\n\t\t\t0: endpoints[0],\n\t\t\t1: endpoints[1],\n\t\t\t2: endpoints[2],\n\t\t}\n\t\tretries = len(endpoints) - 1 // not quite enough retries\n\t\trr      = lb.NewRoundRobin(endpointer)\n\t\tctx     = context.Background()\n\t)\n\tif _, err := lb.Retry(retries, time.Second, rr)(ctx, struct{}{}); err == nil {\n\t\tt.Errorf(\"expected error two, got none\")\n\t}\n}\n\nfunc TestRetryMaxSuccess(t *testing.T) {\n\tvar (\n\t\tendpoints = []endpoint.Endpoint{\n\t\t\tfunc(context.Context, interface{}) (interface{}, error) { return nil, errors.New(\"error one\") },\n\t\t\tfunc(context.Context, interface{}) (interface{}, error) { return nil, errors.New(\"error two\") },\n\t\t\tfunc(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil /* OK */ },\n\t\t}\n\t\tendpointer = sd.FixedEndpointer{\n\t\t\t0: endpoints[0],\n\t\t\t1: endpoints[1],\n\t\t\t2: endpoints[2],\n\t\t}\n\t\tretries = len(endpoints) // exactly enough retries\n\t\trr      = lb.NewRoundRobin(endpointer)\n\t\tctx     = context.Background()\n\t)\n\tif _, err := lb.Retry(retries, time.Second, rr)(ctx, struct{}{}); err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc TestRetryTimeout(t *testing.T) {\n\tvar (\n\t\tstep    = make(chan struct{})\n\t\te       = func(context.Context, interface{}) (interface{}, error) { <-step; return struct{}{}, nil }\n\t\ttimeout = time.Millisecond\n\t\tretry   = lb.Retry(999, timeout, lb.NewRoundRobin(sd.FixedEndpointer{0: e}))\n\t\terrs    = make(chan error, 1)\n\t\tinvoke  = func() { _, err := retry(context.Background(), struct{}{}); errs <- err }\n\t)\n\n\tgo func() { step <- struct{}{} }() // queue up a flush of the endpoint\n\tinvoke()                           // invoke the endpoint and trigger the flush\n\tif err := <-errs; err != nil {     // that should succeed\n\t\tt.Error(err)\n\t}\n\n\tgo func() { time.Sleep(10 * timeout); step <- struct{}{} }() // a delayed flush\n\tinvoke()                                                     // invoke the endpoint\n\tif err := <-errs; err != context.DeadlineExceeded {          // that should not succeed\n\t\tt.Errorf(\"wanted %v, got none\", context.DeadlineExceeded)\n\t}\n}\n\nfunc TestAbortEarlyCustomMessage(t *testing.T) {\n\tvar (\n\t\tmyErr     = errors.New(\"aborting early\")\n\t\tcb        = func(int, error) (bool, error) { return false, myErr }\n\t\tendpoints = sd.FixedEndpointer{} // no endpoints\n\t\trr        = lb.NewRoundRobin(endpoints)\n\t\tretry     = lb.RetryWithCallback(time.Second, rr, cb) // lots of retries\n\t\tctx       = context.Background()\n\t)\n\t_, err := retry(ctx, struct{}{})\n\tif want, have := myErr, err.(lb.RetryError).Final; want != have {\n\t\tt.Errorf(\"want %v, have %v\", want, have)\n\t}\n}\n\nfunc TestErrorPassedUnchangedToCallback(t *testing.T) {\n\tvar (\n\t\tmyErr = errors.New(\"my custom error\")\n\t\tcb    = func(_ int, err error) (bool, error) {\n\t\t\tif want, have := myErr, err; want != have {\n\t\t\t\tt.Errorf(\"want %v, have %v\", want, have)\n\t\t\t}\n\t\t\treturn false, nil\n\t\t}\n\t\tendpoint = func(ctx context.Context, request interface{}) (interface{}, error) {\n\t\t\treturn nil, myErr\n\t\t}\n\t\tendpoints = sd.FixedEndpointer{endpoint} // no endpoints\n\t\trr        = lb.NewRoundRobin(endpoints)\n\t\tretry     = lb.RetryWithCallback(time.Second, rr, cb) // lots of retries\n\t\tctx       = context.Background()\n\t)\n\t_, err := retry(ctx, struct{}{})\n\tif want, have := myErr, err.(lb.RetryError).Final; want != have {\n\t\tt.Errorf(\"want %v, have %v\", want, have)\n\t}\n}\n\nfunc TestHandleNilCallback(t *testing.T) {\n\tvar (\n\t\tendpointer = sd.FixedEndpointer{\n\t\t\tfunc(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil /* OK */ },\n\t\t}\n\t\trr  = lb.NewRoundRobin(endpointer)\n\t\tctx = context.Background()\n\t)\n\tretry := lb.RetryWithCallback(time.Second, rr, nil)\n\tif _, err := retry(ctx, struct{}{}); err != nil {\n\t\tt.Error(err)\n\t}\n}\n"
  },
  {
    "path": "sd/lb/round_robin.go",
    "content": "package lb\n\nimport (\n\t\"sync/atomic\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/sd\"\n)\n\n// NewRoundRobin returns a load balancer that returns services in sequence.\nfunc NewRoundRobin(s sd.Endpointer) Balancer {\n\treturn &roundRobin{\n\t\ts: s,\n\t\tc: 0,\n\t}\n}\n\ntype roundRobin struct {\n\ts sd.Endpointer\n\tc uint64\n}\n\nfunc (rr *roundRobin) Endpoint() (endpoint.Endpoint, error) {\n\tendpoints, err := rr.s.Endpoints()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(endpoints) <= 0 {\n\t\treturn nil, ErrNoEndpoints\n\t}\n\told := atomic.AddUint64(&rr.c, 1) - 1\n\tidx := old % uint64(len(endpoints))\n\treturn endpoints[idx], nil\n}\n"
  },
  {
    "path": "sd/lb/round_robin_test.go",
    "content": "package lb\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/sd\"\n)\n\nfunc TestRoundRobin(t *testing.T) {\n\tvar (\n\t\tcounts    = []int{0, 0, 0}\n\t\tendpoints = []endpoint.Endpoint{\n\t\t\tfunc(context.Context, interface{}) (interface{}, error) { counts[0]++; return struct{}{}, nil },\n\t\t\tfunc(context.Context, interface{}) (interface{}, error) { counts[1]++; return struct{}{}, nil },\n\t\t\tfunc(context.Context, interface{}) (interface{}, error) { counts[2]++; return struct{}{}, nil },\n\t\t}\n\t)\n\n\tendpointer := sd.FixedEndpointer(endpoints)\n\tbalancer := NewRoundRobin(endpointer)\n\n\tfor i, want := range [][]int{\n\t\t{1, 0, 0},\n\t\t{1, 1, 0},\n\t\t{1, 1, 1},\n\t\t{2, 1, 1},\n\t\t{2, 2, 1},\n\t\t{2, 2, 2},\n\t\t{3, 2, 2},\n\t} {\n\t\tendpoint, err := balancer.Endpoint()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tendpoint(context.Background(), struct{}{})\n\t\tif have := counts; !reflect.DeepEqual(want, have) {\n\t\t\tt.Fatalf(\"%d: want %v, have %v\", i, want, have)\n\t\t}\n\t}\n}\n\nfunc TestRoundRobinNoEndpoints(t *testing.T) {\n\tendpointer := sd.FixedEndpointer{}\n\tbalancer := NewRoundRobin(endpointer)\n\t_, err := balancer.Endpoint()\n\tif want, have := ErrNoEndpoints, err; want != have {\n\t\tt.Errorf(\"want %v, have %v\", want, have)\n\t}\n}\n\nfunc TestRoundRobinNoRace(t *testing.T) {\n\tbalancer := NewRoundRobin(sd.FixedEndpointer([]endpoint.Endpoint{\n\t\tendpoint.Nop,\n\t\tendpoint.Nop,\n\t\tendpoint.Nop,\n\t\tendpoint.Nop,\n\t\tendpoint.Nop,\n\t}))\n\n\tvar (\n\t\tn     = 100\n\t\tdone  = make(chan struct{})\n\t\twg    sync.WaitGroup\n\t\tcount uint64\n\t)\n\n\twg.Add(n)\n\n\tfor i := 0; i < n; i++ {\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tfor {\n\t\t\t\tselect {\n\t\t\t\tcase <-done:\n\t\t\t\t\treturn\n\t\t\t\tdefault:\n\t\t\t\t\t_, _ = balancer.Endpoint()\n\t\t\t\t\tatomic.AddUint64(&count, 1)\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t}\n\n\ttime.Sleep(time.Second)\n\tclose(done)\n\twg.Wait()\n\n\tt.Logf(\"made %d calls\", atomic.LoadUint64(&count))\n}\n"
  },
  {
    "path": "sd/registrar.go",
    "content": "package sd\n\n// Registrar registers instance information to a service discovery system when\n// an instance becomes alive and healthy, and deregisters that information when\n// the service becomes unhealthy or goes away.\n//\n// Registrar implementations exist for various service discovery systems. Note\n// that identifying instance information (e.g. host:port) must be given via the\n// concrete constructor; this interface merely signals lifecycle changes.\ntype Registrar interface {\n\tRegister()\n\tDeregister()\n}\n"
  },
  {
    "path": "sd/zk/client.go",
    "content": "package zk\n\nimport (\n\t\"errors\"\n\t\"net\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/go-zookeeper/zk\"\n\n\t\"github.com/go-kit/log\"\n)\n\n// DefaultACL is the default ACL to use for creating znodes.\nvar (\n\tDefaultACL            = zk.WorldACL(zk.PermAll)\n\tErrInvalidCredentials = errors.New(\"invalid credentials provided\")\n\tErrClientClosed       = errors.New(\"client service closed\")\n\tErrNotRegistered      = errors.New(\"not registered\")\n\tErrNodeNotFound       = errors.New(\"node not found\")\n)\n\nconst (\n\t// DefaultConnectTimeout is the default timeout to establish a connection to\n\t// a ZooKeeper node.\n\tDefaultConnectTimeout = 2 * time.Second\n\t// DefaultSessionTimeout is the default timeout to keep the current\n\t// ZooKeeper session alive during a temporary disconnect.\n\tDefaultSessionTimeout = 5 * time.Second\n)\n\n// Client is a wrapper around a lower level ZooKeeper client implementation.\ntype Client interface {\n\t// GetEntries should query the provided path in ZooKeeper, place a watch on\n\t// it and retrieve data from its current child nodes.\n\tGetEntries(path string) ([]string, <-chan zk.Event, error)\n\t// CreateParentNodes should try to create the path in case it does not exist\n\t// yet on ZooKeeper.\n\tCreateParentNodes(path string) error\n\t// Register a service with ZooKeeper.\n\tRegister(s *Service) error\n\t// Deregister a service with ZooKeeper.\n\tDeregister(s *Service) error\n\t// Stop should properly shutdown the client implementation\n\tStop()\n}\n\ntype clientConfig struct {\n\tlogger          log.Logger\n\tacl             []zk.ACL\n\tcredentials     []byte\n\tconnectTimeout  time.Duration\n\tsessionTimeout  time.Duration\n\trootNodePayload [][]byte\n\teventHandler    func(zk.Event)\n}\n\n// Option functions enable friendly APIs.\ntype Option func(*clientConfig) error\n\ntype client struct {\n\t*zk.Conn\n\tclientConfig\n\tactive bool\n\tquit   chan struct{}\n}\n\n// ACL returns an Option specifying a non-default ACL for creating parent nodes.\nfunc ACL(acl []zk.ACL) Option {\n\treturn func(c *clientConfig) error {\n\t\tc.acl = acl\n\t\treturn nil\n\t}\n}\n\n// Credentials returns an Option specifying a user/password combination which\n// the client will use to authenticate itself with.\nfunc Credentials(user, pass string) Option {\n\treturn func(c *clientConfig) error {\n\t\tif user == \"\" || pass == \"\" {\n\t\t\treturn ErrInvalidCredentials\n\t\t}\n\t\tc.credentials = []byte(user + \":\" + pass)\n\t\treturn nil\n\t}\n}\n\n// ConnectTimeout returns an Option specifying a non-default connection timeout\n// when we try to establish a connection to a ZooKeeper server.\nfunc ConnectTimeout(t time.Duration) Option {\n\treturn func(c *clientConfig) error {\n\t\tif t.Seconds() < 1 {\n\t\t\treturn errors.New(\"invalid connect timeout (minimum value is 1 second)\")\n\t\t}\n\t\tc.connectTimeout = t\n\t\treturn nil\n\t}\n}\n\n// SessionTimeout returns an Option specifying a non-default session timeout.\nfunc SessionTimeout(t time.Duration) Option {\n\treturn func(c *clientConfig) error {\n\t\tif t.Seconds() < 1 {\n\t\t\treturn errors.New(\"invalid session timeout (minimum value is 1 second)\")\n\t\t}\n\t\tc.sessionTimeout = t\n\t\treturn nil\n\t}\n}\n\n// Payload returns an Option specifying non-default data values for each znode\n// created by CreateParentNodes.\nfunc Payload(payload [][]byte) Option {\n\treturn func(c *clientConfig) error {\n\t\tc.rootNodePayload = payload\n\t\treturn nil\n\t}\n}\n\n// EventHandler returns an Option specifying a callback function to handle\n// incoming zk.Event payloads (ZooKeeper connection events).\nfunc EventHandler(handler func(zk.Event)) Option {\n\treturn func(c *clientConfig) error {\n\t\tc.eventHandler = handler\n\t\treturn nil\n\t}\n}\n\n// NewClient returns a ZooKeeper client with a connection to the server cluster.\n// It will return an error if the server cluster cannot be resolved.\nfunc NewClient(servers []string, logger log.Logger, options ...Option) (Client, error) {\n\tdefaultEventHandler := func(event zk.Event) {\n\t\tlogger.Log(\"eventtype\", event.Type.String(), \"server\", event.Server, \"state\", event.State.String(), \"err\", event.Err)\n\t}\n\tconfig := clientConfig{\n\t\tacl:            DefaultACL,\n\t\tconnectTimeout: DefaultConnectTimeout,\n\t\tsessionTimeout: DefaultSessionTimeout,\n\t\teventHandler:   defaultEventHandler,\n\t\tlogger:         logger,\n\t}\n\tfor _, option := range options {\n\t\tif err := option(&config); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\t// dialer overrides the default ZooKeeper library Dialer so we can configure\n\t// the connectTimeout. The current library has a hardcoded value of 1 second\n\t// and there are reports of race conditions, due to slow DNS resolvers and\n\t// other network latency issues.\n\tdialer := func(network, address string, _ time.Duration) (net.Conn, error) {\n\t\treturn net.DialTimeout(network, address, config.connectTimeout)\n\t}\n\tconn, eventc, err := zk.Connect(servers, config.sessionTimeout, withLogger(logger), zk.WithDialer(dialer))\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif len(config.credentials) > 0 {\n\t\terr = conn.AddAuth(\"digest\", config.credentials)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tc := &client{conn, config, true, make(chan struct{})}\n\n\t// Start listening for incoming Event payloads and callback the set\n\t// eventHandler.\n\tgo func() {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase event := <-eventc:\n\t\t\t\tconfig.eventHandler(event)\n\t\t\tcase <-c.quit:\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\treturn c, nil\n}\n\n// CreateParentNodes implements the ZooKeeper Client interface.\nfunc (c *client) CreateParentNodes(path string) error {\n\tif !c.active {\n\t\treturn ErrClientClosed\n\t}\n\tif path[0] != '/' {\n\t\treturn zk.ErrInvalidPath\n\t}\n\tpayload := []byte(\"\")\n\tpathString := \"\"\n\tpathNodes := strings.Split(path, \"/\")\n\tfor i := 1; i < len(pathNodes); i++ {\n\t\tif i <= len(c.rootNodePayload) {\n\t\t\tpayload = c.rootNodePayload[i-1]\n\t\t} else {\n\t\t\tpayload = []byte(\"\")\n\t\t}\n\t\tpathString += \"/\" + pathNodes[i]\n\t\t_, err := c.Create(pathString, payload, 0, c.acl)\n\t\t// not being able to create the node because it exists or not having\n\t\t// sufficient rights is not an issue. It is ok for the node to already\n\t\t// exist and/or us to only have read rights\n\t\tif err != nil && err != zk.ErrNodeExists && err != zk.ErrNoAuth {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// GetEntries implements the ZooKeeper Client interface.\nfunc (c *client) GetEntries(path string) ([]string, <-chan zk.Event, error) {\n\t// retrieve list of child nodes for given path and add watch to path\n\tznodes, _, eventc, err := c.ChildrenW(path)\n\n\tif err != nil {\n\t\treturn nil, eventc, err\n\t}\n\n\tvar resp []string\n\tfor _, znode := range znodes {\n\t\t// retrieve payload for child znode and add to response array\n\t\tif data, _, err := c.Get(path + \"/\" + znode); err == nil {\n\t\t\tresp = append(resp, string(data))\n\t\t}\n\t}\n\treturn resp, eventc, nil\n}\n\n// Register implements the ZooKeeper Client interface.\nfunc (c *client) Register(s *Service) error {\n\tif s.Path[len(s.Path)-1] != '/' {\n\t\ts.Path += \"/\"\n\t}\n\tpath := s.Path + s.Name\n\tif err := c.CreateParentNodes(path); err != nil {\n\t\treturn err\n\t}\n\tif path[len(path)-1] != '/' {\n\t\tpath += \"/\"\n\t}\n\tnode, err := c.CreateProtectedEphemeralSequential(path, s.Data, c.acl)\n\tif err != nil {\n\t\treturn err\n\t}\n\ts.node = node\n\treturn nil\n}\n\n// Deregister implements the ZooKeeper Client interface.\nfunc (c *client) Deregister(s *Service) error {\n\tif s.node == \"\" {\n\t\treturn ErrNotRegistered\n\t}\n\tpath := s.Path + s.Name\n\tfound, stat, err := c.Exists(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif !found {\n\t\treturn ErrNodeNotFound\n\t}\n\tif err := c.Delete(path, stat.Version); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// Stop implements the ZooKeeper Client interface.\nfunc (c *client) Stop() {\n\tc.active = false\n\tclose(c.quit)\n\tc.Close()\n}\n"
  },
  {
    "path": "sd/zk/client_test.go",
    "content": "package zk\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\t\"time\"\n\n\tstdzk \"github.com/go-zookeeper/zk\"\n\n\t\"github.com/go-kit/log\"\n)\n\nfunc TestNewClient(t *testing.T) {\n\tvar (\n\t\tacl            = stdzk.WorldACL(stdzk.PermRead)\n\t\tconnectTimeout = 3 * time.Second\n\t\tsessionTimeout = 20 * time.Second\n\t\tpayload        = [][]byte{[]byte(\"Payload\"), []byte(\"Test\")}\n\t)\n\n\tc, err := NewClient(\n\t\t[]string{\"FailThisInvalidHost!!!\"},\n\t\tlog.NewNopLogger(),\n\t)\n\tif err == nil {\n\t\tt.Errorf(\"expected error, got nil\")\n\t}\n\n\thasFired := false\n\tcalledEventHandler := make(chan struct{})\n\teventHandler := func(event stdzk.Event) {\n\t\tif !hasFired {\n\t\t\t// test is successful if this function has fired at least once\n\t\t\thasFired = true\n\t\t\tclose(calledEventHandler)\n\t\t}\n\t}\n\n\tc, err = NewClient(\n\t\t[]string{\"localhost\"},\n\t\tlog.NewNopLogger(),\n\t\tACL(acl),\n\t\tConnectTimeout(connectTimeout),\n\t\tSessionTimeout(sessionTimeout),\n\t\tPayload(payload),\n\t\tEventHandler(eventHandler),\n\t)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer c.Stop()\n\n\tclientImpl, ok := c.(*client)\n\tif !ok {\n\t\tt.Fatal(\"retrieved incorrect Client implementation\")\n\t}\n\tif want, have := acl, clientImpl.acl; want[0] != have[0] {\n\t\tt.Errorf(\"want %+v, have %+v\", want, have)\n\t}\n\tif want, have := connectTimeout, clientImpl.connectTimeout; want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\tif want, have := sessionTimeout, clientImpl.sessionTimeout; want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\tif want, have := payload, clientImpl.rootNodePayload; !bytes.Equal(want[0], have[0]) || !bytes.Equal(want[1], have[1]) {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t}\n\n\tselect {\n\tcase <-calledEventHandler:\n\tcase <-time.After(100 * time.Millisecond):\n\t\tt.Errorf(\"event handler never called\")\n\t}\n}\n\nfunc TestOptions(t *testing.T) {\n\t_, err := NewClient([]string{\"localhost\"}, log.NewNopLogger(), Credentials(\"valid\", \"credentials\"))\n\tif err != nil && err != stdzk.ErrNoServer {\n\t\tt.Errorf(\"unexpected error: %v\", err)\n\t}\n\n\t_, err = NewClient([]string{\"localhost\"}, log.NewNopLogger(), Credentials(\"nopass\", \"\"))\n\tif want, have := err, ErrInvalidCredentials; want != have {\n\t\tt.Errorf(\"want %v, have %v\", want, have)\n\t}\n\n\t_, err = NewClient([]string{\"localhost\"}, log.NewNopLogger(), ConnectTimeout(0))\n\tif err == nil {\n\t\tt.Errorf(\"expected connect timeout error\")\n\t}\n\n\t_, err = NewClient([]string{\"localhost\"}, log.NewNopLogger(), SessionTimeout(0))\n\tif err == nil {\n\t\tt.Errorf(\"expected connect timeout error\")\n\t}\n}\n\nfunc TestCreateParentNodes(t *testing.T) {\n\tpayload := [][]byte{[]byte(\"Payload\"), []byte(\"Test\")}\n\n\tc, err := NewClient([]string{\"localhost:65500\"}, log.NewNopLogger())\n\tif err != nil {\n\t\tt.Errorf(\"unexpected error: %v\", err)\n\t}\n\tif c == nil {\n\t\tt.Fatal(\"expected new Client, got nil\")\n\t}\n\n\ts, err := NewInstancer(c, \"/validpath\", log.NewNopLogger())\n\tif err != stdzk.ErrNoServer {\n\t\tt.Errorf(\"unexpected error: %v\", err)\n\t}\n\tif s != nil {\n\t\tt.Error(\"expected failed new Instancer\")\n\t}\n\n\ts, err = NewInstancer(c, \"invalidpath\", log.NewNopLogger())\n\tif err != stdzk.ErrInvalidPath {\n\t\tt.Errorf(\"unexpected error: %v\", err)\n\t}\n\t_, _, err = c.GetEntries(\"/validpath\")\n\tif err != stdzk.ErrNoServer {\n\t\tt.Errorf(\"unexpected error: %v\", err)\n\t}\n\n\tc.Stop()\n\n\terr = c.CreateParentNodes(\"/validpath\")\n\tif err != ErrClientClosed {\n\t\tt.Errorf(\"unexpected error: %v\", err)\n\t}\n\n\ts, err = NewInstancer(c, \"/validpath\", log.NewNopLogger())\n\tif err != ErrClientClosed {\n\t\tt.Errorf(\"unexpected error: %v\", err)\n\t}\n\tif s != nil {\n\t\tt.Error(\"expected failed new Instancer\")\n\t}\n\n\tc, err = NewClient([]string{\"localhost:65500\"}, log.NewNopLogger(), Payload(payload))\n\tif err != nil {\n\t\tt.Errorf(\"unexpected error: %v\", err)\n\t}\n\tif c == nil {\n\t\tt.Fatal(\"expected new Client, got nil\")\n\t}\n\n\ts, err = NewInstancer(c, \"/validpath\", log.NewNopLogger())\n\tif err != stdzk.ErrNoServer {\n\t\tt.Errorf(\"unexpected error: %v\", err)\n\t}\n\tif s != nil {\n\t\tt.Error(\"expected failed new Instancer\")\n\t}\n}\n"
  },
  {
    "path": "sd/zk/doc.go",
    "content": "// Package zk provides Instancer and Registrar implementations for ZooKeeper.\npackage zk\n"
  },
  {
    "path": "sd/zk/instancer.go",
    "content": "package zk\n\nimport (\n\t\"github.com/go-zookeeper/zk\"\n\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/kit/sd/internal/instance\"\n\t\"github.com/go-kit/log\"\n)\n\n// Instancer yield instances stored in a certain ZooKeeper path. Any kind of\n// change in that path is watched and will update the subscribers.\ntype Instancer struct {\n\tcache  *instance.Cache\n\tclient Client\n\tpath   string\n\tlogger log.Logger\n\tquitc  chan struct{}\n}\n\n// NewInstancer returns a ZooKeeper Instancer. ZooKeeper will start watching\n// the given path for changes and update the Instancer endpoints.\nfunc NewInstancer(c Client, path string, logger log.Logger) (*Instancer, error) {\n\ts := &Instancer{\n\t\tcache:  instance.NewCache(),\n\t\tclient: c,\n\t\tpath:   path,\n\t\tlogger: logger,\n\t\tquitc:  make(chan struct{}),\n\t}\n\n\terr := s.client.CreateParentNodes(s.path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinstances, eventc, err := s.client.GetEntries(s.path)\n\tif err != nil {\n\t\tlogger.Log(\"path\", s.path, \"msg\", \"failed to retrieve entries\", \"err\", err)\n\t\t// other implementations continue here, but we exit because we don't know if eventc is valid\n\t\treturn nil, err\n\t}\n\tlogger.Log(\"path\", s.path, \"instances\", len(instances))\n\ts.cache.Update(sd.Event{Instances: instances})\n\n\tgo s.loop(eventc)\n\n\treturn s, nil\n}\n\nfunc (s *Instancer) loop(eventc <-chan zk.Event) {\n\tvar (\n\t\tinstances []string\n\t\terr       error\n\t)\n\tfor {\n\t\tselect {\n\t\tcase <-eventc:\n\t\t\t// We received a path update notification. Call GetEntries to\n\t\t\t// retrieve child node data, and set a new watch, as ZK watches are\n\t\t\t// one-time triggers.\n\t\t\tinstances, eventc, err = s.client.GetEntries(s.path)\n\t\t\tif err != nil {\n\t\t\t\ts.logger.Log(\"path\", s.path, \"msg\", \"failed to retrieve entries\", \"err\", err)\n\t\t\t\ts.cache.Update(sd.Event{Err: err})\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ts.logger.Log(\"path\", s.path, \"instances\", len(instances))\n\t\t\ts.cache.Update(sd.Event{Instances: instances})\n\n\t\tcase <-s.quitc:\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// Stop terminates the Instancer.\nfunc (s *Instancer) Stop() {\n\tclose(s.quitc)\n}\n\n// Register implements Instancer.\nfunc (s *Instancer) Register(ch chan<- sd.Event) {\n\ts.cache.Register(ch)\n}\n\n// Deregister implements Instancer.\nfunc (s *Instancer) Deregister(ch chan<- sd.Event) {\n\ts.cache.Deregister(ch)\n}\n\n// state returns the current state of instance.Cache, only for testing\nfunc (s *Instancer) state() sd.Event {\n\treturn s.cache.State()\n}\n"
  },
  {
    "path": "sd/zk/instancer_test.go",
    "content": "package zk\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/sd\"\n)\n\nvar _ sd.Instancer = (*Instancer)(nil) // API check\n\nfunc TestInstancer(t *testing.T) {\n\tclient := newFakeClient()\n\n\tinstancer, err := NewInstancer(client, path, logger)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create new Instancer: %v\", err)\n\t}\n\tdefer instancer.Stop()\n\tendpointer := sd.NewEndpointer(instancer, newFactory(\"\"), logger)\n\n\tif _, err := endpointer.Endpoints(); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestBadFactory(t *testing.T) {\n\tclient := newFakeClient()\n\n\tinstancer, err := NewInstancer(client, path, logger)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create new Instancer: %v\", err)\n\t}\n\tdefer instancer.Stop()\n\tendpointer := sd.NewEndpointer(instancer, newFactory(\"kaboom\"), logger)\n\n\t// instance1 came online\n\tclient.AddService(path+\"/instance1\", \"kaboom\")\n\n\t// instance2 came online\n\tclient.AddService(path+\"/instance2\", \"zookeeper_node_data\")\n\n\tif err = asyncTest(100*time.Millisecond, 1, endpointer); err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc TestServiceUpdate(t *testing.T) {\n\tclient := newFakeClient()\n\n\tinstancer, err := NewInstancer(client, path, logger)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create new Instancer: %v\", err)\n\t}\n\tdefer instancer.Stop()\n\tendpointer := sd.NewEndpointer(instancer, newFactory(\"\"), logger)\n\n\tendpoints, err := endpointer.Endpoints()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif want, have := 0, len(endpoints); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\t// instance1 came online\n\tclient.AddService(path+\"/instance1\", \"zookeeper_node_data1\")\n\n\t// instance2 came online\n\tclient.AddService(path+\"/instance2\", \"zookeeper_node_data2\")\n\n\t// we should have 2 instances\n\tif err = asyncTest(100*time.Millisecond, 2, endpointer); err != nil {\n\t\tt.Error(err)\n\t}\n\n\t// TODO(pb): this bit is flaky\n\t//\n\t//// watch triggers an error...\n\t//client.SendErrorOnWatch()\n\t//\n\t//// test if error was consumed\n\t//if err = client.ErrorIsConsumedWithin(100 * time.Millisecond); err != nil {\n\t//\tt.Error(err)\n\t//}\n\n\t// instance3 came online\n\tclient.AddService(path+\"/instance3\", \"zookeeper_node_data3\")\n\n\t// we should have 3 instances\n\tif err = asyncTest(100*time.Millisecond, 3, endpointer); err != nil {\n\t\tt.Error(err)\n\t}\n\n\t// instance1 goes offline\n\tclient.RemoveService(path + \"/instance1\")\n\n\t// instance2 goes offline\n\tclient.RemoveService(path + \"/instance2\")\n\n\t// we should have 1 instance\n\tif err = asyncTest(100*time.Millisecond, 1, endpointer); err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc TestBadInstancerCreate(t *testing.T) {\n\tclient := newFakeClient()\n\tclient.SendErrorOnWatch()\n\n\tinstancer, err := NewInstancer(client, path, logger)\n\tif err == nil {\n\t\tt.Error(\"expected error on new Instancer\")\n\t}\n\tif instancer != nil {\n\t\tt.Error(\"expected Instancer not to be created\")\n\t}\n\tinstancer, err = NewInstancer(client, \"BadPath\", logger)\n\tif err == nil {\n\t\tt.Error(\"expected error on new Instancer\")\n\t}\n\tif instancer != nil {\n\t\tt.Error(\"expected Instancer not to be created\")\n\t}\n}\n"
  },
  {
    "path": "sd/zk/integration_test.go",
    "content": "// +build integration\n\npackage zk\n\nimport (\n\t\"bytes\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\tstdzk \"github.com/go-zookeeper/zk\"\n)\n\nvar (\n\thost []string\n)\n\nfunc TestMain(m *testing.M) {\n\tzkAddr := os.Getenv(\"ZK_ADDR\")\n\tif zkAddr != \"\" {\n\t\thost = []string{zkAddr}\n\t}\n\tm.Run()\n}\n\nfunc TestCreateParentNodesOnServer(t *testing.T) {\n\tif len(host) == 0 {\n\t\tt.Skip(\"ZK_ADDR not set; skipping integration test\")\n\t}\n\tpayload := [][]byte{[]byte(\"Payload\"), []byte(\"Test\")}\n\tc1, err := NewClient(host, logger, Payload(payload))\n\tif err != nil {\n\t\tt.Fatalf(\"Connect returned error: %v\", err)\n\t}\n\tif c1 == nil {\n\t\tt.Fatal(\"Expected pointer to client, got nil\")\n\t}\n\tdefer c1.Stop()\n\n\tinstancer, err := NewInstancer(c1, path, logger)\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to create Subscriber: %v\", err)\n\t}\n\tdefer instancer.Stop()\n\n\tstate := instancer.state()\n\tif state.Err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif want, have := 0, len(state.Instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\tc2, err := NewClient(host, logger)\n\tif err != nil {\n\t\tt.Fatalf(\"Connect returned error: %v\", err)\n\t}\n\tdefer c2.Stop()\n\tdata, _, err := c2.(*client).Get(path)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\t// test Client implementation of CreateParentNodes. It should have created\n\t// our payload\n\tif bytes.Compare(data, payload[1]) != 0 {\n\t\tt.Errorf(\"want %s, have %s\", payload[1], data)\n\t}\n\n}\n\nfunc TestCreateBadParentNodesOnServer(t *testing.T) {\n\tif len(host) == 0 {\n\t\tt.Skip(\"ZK_ADDR not set; skipping integration test\")\n\t}\n\tc, _ := NewClient(host, logger)\n\tdefer c.Stop()\n\n\t_, err := NewInstancer(c, \"invalid/path\", logger)\n\n\tif want, have := stdzk.ErrInvalidPath, err; want != have {\n\t\tt.Errorf(\"want %v, have %v\", want, have)\n\t}\n}\n\nfunc TestCredentials1(t *testing.T) {\n\tif len(host) == 0 {\n\t\tt.Skip(\"ZK_ADDR not set; skipping integration test\")\n\t}\n\tacl := stdzk.DigestACL(stdzk.PermAll, \"user\", \"secret\")\n\tc, _ := NewClient(host, logger, ACL(acl), Credentials(\"user\", \"secret\"))\n\tdefer c.Stop()\n\n\t_, err := NewInstancer(c, \"/acl-issue-test\", logger)\n\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestCredentials2(t *testing.T) {\n\tif len(host) == 0 {\n\t\tt.Skip(\"ZK_ADDR not set; skipping integration test\")\n\t}\n\tacl := stdzk.DigestACL(stdzk.PermAll, \"user\", \"secret\")\n\tc, _ := NewClient(host, logger, ACL(acl))\n\tdefer c.Stop()\n\n\t_, err := NewInstancer(c, \"/acl-issue-test\", logger)\n\n\tif err != stdzk.ErrNoAuth {\n\t\tt.Errorf(\"want %v, have %v\", stdzk.ErrNoAuth, err)\n\t}\n}\n\nfunc TestConnection(t *testing.T) {\n\tif len(host) == 0 {\n\t\tt.Skip(\"ZK_ADDR not set; skipping integration test\")\n\t}\n\tc, _ := NewClient(host, logger)\n\tc.Stop()\n\n\t_, err := NewInstancer(c, \"/acl-issue-test\", logger)\n\n\tif err != ErrClientClosed {\n\t\tt.Errorf(\"want %v, have %v\", ErrClientClosed, err)\n\t}\n}\n\nfunc TestGetEntriesOnServer(t *testing.T) {\n\tif len(host) == 0 {\n\t\tt.Skip(\"ZK_ADDR not set; skipping integration test\")\n\t}\n\tvar instancePayload = \"10.0.3.204:8002\"\n\n\tc1, err := NewClient(host, logger)\n\tif err != nil {\n\t\tt.Fatalf(\"Connect returned error: %v\", err)\n\t}\n\n\tdefer c1.Stop()\n\n\tc2, err := NewClient(host, logger)\n\ts, err := NewInstancer(c2, path, logger)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer c2.Stop()\n\n\tinstance1 := &Service{\n\t\tPath: path,\n\t\tName: \"instance1\",\n\t\tData: []byte(instancePayload),\n\t}\n\tif err = c2.Register(instance1); err != nil {\n\t\tt.Fatalf(\"Unable to create test ephemeral znode 1: %+v\", err)\n\t}\n\tinstance2 := &Service{\n\t\tPath: path,\n\t\tName: \"instance2\",\n\t\tData: []byte(instancePayload),\n\t}\n\tif err = c2.Register(instance2); err != nil {\n\t\tt.Fatalf(\"Unable to create test ephemeral znode 2: %+v\", err)\n\t}\n\n\ttime.Sleep(50 * time.Millisecond)\n\n\tstate := s.state()\n\tif state.Err != nil {\n\t\tt.Fatal(state.Err)\n\t}\n\tif want, have := 2, len(state.Instances); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\nfunc TestGetEntriesPayloadOnServer(t *testing.T) {\n\tt.Skip(\"FLAKY\")\n\n\tif len(host) == 0 {\n\t\tt.Skip(\"ZK_ADDR not set; skipping integration test\")\n\t}\n\n\tc, err := NewClient(host, logger)\n\tif err != nil {\n\t\tt.Fatalf(\"Connect returned error: %v\", err)\n\t}\n\n\t_, eventc, err := c.GetEntries(path)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tinstance3 := Service{\n\t\tPath: path,\n\t\tName: \"instance3\",\n\t\tData: []byte(\"just some payload\"),\n\t}\n\n\tregistrar := NewRegistrar(c, instance3, logger)\n\tregistrar.Register()\n\n\tselect {\n\tcase event := <-eventc:\n\t\tif want, have := stdzk.EventNodeChildrenChanged.String(), event.Type.String(); want != have {\n\t\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t\t}\n\tcase <-time.After(10 * time.Second):\n\t\tt.Errorf(\"expected incoming watch event, timeout occurred\")\n\t}\n\n\t_, eventc, err = c.GetEntries(path)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tregistrar.Deregister()\n\tselect {\n\tcase event := <-eventc:\n\t\tif want, have := stdzk.EventNodeChildrenChanged.String(), event.Type.String(); want != have {\n\t\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t\t}\n\tcase <-time.After(100 * time.Millisecond):\n\t\tt.Errorf(\"expected incoming watch event, timeout occurred\")\n\t}\n\n}\n"
  },
  {
    "path": "sd/zk/logwrapper.go",
    "content": "package zk\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/go-zookeeper/zk\"\n\n\t\"github.com/go-kit/log\"\n)\n\n// wrapLogger wraps a Go kit logger so we can use it as the logging service for\n// the ZooKeeper library, which expects a Printf method to be available.\ntype wrapLogger struct {\n\tlog.Logger\n}\n\nfunc (logger wrapLogger) Printf(format string, args ...interface{}) {\n\tlogger.Log(\"msg\", fmt.Sprintf(format, args...))\n}\n\n// withLogger replaces the ZooKeeper library's default logging service with our\n// own Go kit logger.\nfunc withLogger(logger log.Logger) func(c *zk.Conn) {\n\treturn func(c *zk.Conn) {\n\t\tc.SetLogger(wrapLogger{logger})\n\t}\n}\n"
  },
  {
    "path": "sd/zk/registrar.go",
    "content": "package zk\n\nimport \"github.com/go-kit/log\"\n\n// Registrar registers service instance liveness information to ZooKeeper.\ntype Registrar struct {\n\tclient  Client\n\tservice Service\n\tlogger  log.Logger\n}\n\n// Service holds the root path, service name and instance identifying data you\n// want to publish to ZooKeeper.\ntype Service struct {\n\tPath string // discovery namespace, example: /myorganization/myplatform/\n\tName string // service name, example: addscv\n\tData []byte // instance data to store for discovery, example: 10.0.2.10:80\n\tnode string // Client will record the ephemeral node name so we can deregister\n}\n\n// NewRegistrar returns a ZooKeeper Registrar acting on the provided catalog\n// registration.\nfunc NewRegistrar(client Client, service Service, logger log.Logger) *Registrar {\n\treturn &Registrar{\n\t\tclient:  client,\n\t\tservice: service,\n\t\tlogger: log.With(logger,\n\t\t\t\"service\", service.Name,\n\t\t\t\"path\", service.Path,\n\t\t\t\"data\", string(service.Data),\n\t\t),\n\t}\n}\n\n// Register implements sd.Registrar interface.\nfunc (r *Registrar) Register() {\n\tif err := r.client.Register(&r.service); err != nil {\n\t\tr.logger.Log(\"err\", err)\n\t} else {\n\t\tr.logger.Log(\"action\", \"register\")\n\t}\n}\n\n// Deregister implements sd.Registrar interface.\nfunc (r *Registrar) Deregister() {\n\tif err := r.client.Deregister(&r.service); err != nil {\n\t\tr.logger.Log(\"err\", err)\n\t} else {\n\t\tr.logger.Log(\"action\", \"deregister\")\n\t}\n}\n"
  },
  {
    "path": "sd/zk/util_test.go",
    "content": "package zk\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-zookeeper/zk\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/log\"\n)\n\nvar (\n\tpath   = \"/gokit.test/service.name\"\n\tlogger = log.NewNopLogger()\n)\n\ntype fakeClient struct {\n\tmtx       sync.Mutex\n\tch        chan zk.Event\n\tresponses map[string]string\n\tresult    bool\n}\n\nfunc newFakeClient() *fakeClient {\n\treturn &fakeClient{\n\t\tch:        make(chan zk.Event, 1),\n\t\tresponses: make(map[string]string),\n\t\tresult:    true,\n\t}\n}\n\nfunc (c *fakeClient) CreateParentNodes(path string) error {\n\tif path == \"BadPath\" {\n\t\treturn errors.New(\"dummy error\")\n\t}\n\treturn nil\n}\n\nfunc (c *fakeClient) GetEntries(path string) ([]string, <-chan zk.Event, error) {\n\tc.mtx.Lock()\n\tdefer c.mtx.Unlock()\n\tif c.result == false {\n\t\tc.result = true\n\t\treturn []string{}, c.ch, errors.New(\"dummy error\")\n\t}\n\tresponses := []string{}\n\tfor _, data := range c.responses {\n\t\tresponses = append(responses, data)\n\t}\n\treturn responses, c.ch, nil\n}\n\nfunc (c *fakeClient) AddService(node, data string) {\n\tc.mtx.Lock()\n\tdefer c.mtx.Unlock()\n\tc.responses[node] = data\n\tc.ch <- zk.Event{}\n}\n\nfunc (c *fakeClient) RemoveService(node string) {\n\tc.mtx.Lock()\n\tdefer c.mtx.Unlock()\n\tdelete(c.responses, node)\n\tc.ch <- zk.Event{}\n}\n\nfunc (c *fakeClient) Register(s *Service) error {\n\treturn nil\n}\n\nfunc (c *fakeClient) Deregister(s *Service) error {\n\treturn nil\n}\n\nfunc (c *fakeClient) SendErrorOnWatch() {\n\tc.mtx.Lock()\n\tdefer c.mtx.Unlock()\n\tc.result = false\n\tc.ch <- zk.Event{}\n}\n\nfunc (c *fakeClient) ErrorIsConsumedWithin(timeout time.Duration) error {\n\tt := time.After(timeout)\n\tfor {\n\t\tselect {\n\t\tcase <-t:\n\t\t\treturn fmt.Errorf(\"expected error not consumed after timeout %s\", timeout)\n\t\tdefault:\n\t\t\tc.mtx.Lock()\n\t\t\tif c.result == false {\n\t\t\t\tc.mtx.Unlock()\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tc.mtx.Unlock()\n\t\t}\n\t}\n}\n\nfunc (c *fakeClient) Stop() {}\n\nfunc newFactory(fakeError string) sd.Factory {\n\treturn func(instance string) (endpoint.Endpoint, io.Closer, error) {\n\t\tif fakeError == instance {\n\t\t\treturn nil, nil, errors.New(fakeError)\n\t\t}\n\t\treturn endpoint.Nop, nil, nil\n\t}\n}\n\nfunc asyncTest(timeout time.Duration, want int, s sd.Endpointer) (err error) {\n\tvar endpoints []endpoint.Endpoint\n\thave := -1 // want can never be <0\n\tt := time.After(timeout)\n\tfor {\n\t\tselect {\n\t\tcase <-t:\n\t\t\treturn fmt.Errorf(\"want %d, have %d (timeout %s)\", want, have, timeout.String())\n\t\tdefault:\n\t\t\tendpoints, err = s.Endpoints()\n\t\t\thave = len(endpoints)\n\t\t\tif err != nil || want == have {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttime.Sleep(timeout / 10)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tracing/README.md",
    "content": "# package tracing\n\n`package tracing` provides [Dapper]-style request tracing to services.\n\n## Rationale\n\nRequest tracing is a fundamental building block for large distributed\napplications. It's instrumental in understanding request flows, identifying\nhot spots, and diagnosing errors. All microservice infrastructures will\nbenefit from request tracing; sufficiently large infrastructures will require\nit.\n\n## Zipkin\n\n[Zipkin] is one of the most used OSS distributed tracing platforms available\nwith support for many different languages and frameworks. Go kit provides\nbindings to the native Go tracing implementation [zipkin-go]. If using Zipkin\nwith Go kit in a polyglot microservices environment, this is the preferred\nbinding to use. Instrumentation exists for `kit/transport/http` and\n`kit/transport/grpc`. The bindings are highlighted in the [addsvc] example. For\nmore information regarding Zipkin feel free to visit [Zipkin's Gitter].\n\n## OpenCensus\n\nGo kit supports transport and endpoint middlewares for the [OpenCensus]\ninstrumentation library. OpenCensus provides a cross language consistent data\nmodel and instrumentation libraries for tracing and metrics. From this data\nmodel it allows exports to various tracing and metrics backends including but\nnot limited to Zipkin, Prometheus, Stackdriver Trace & Monitoring, Jaeger,\nAWS X-Ray and Datadog. Go kit uses the [opencensus-go] implementation to power\nits middlewares.\n\n## OpenTracing\n\nGo kit supports the [OpenTracing] API and uses the [opentracing-go] package to\nprovide tracing middlewares for its servers and clients. Currently OpenTracing\ninstrumentation exists for `kit/transport/http` and `kit/transport/grpc`.\n\nSince [OpenTracing] is an effort to provide a generic API, Go kit should support\na multitude of tracing backends. If a Tracer implementation or OpenTracing\nbridge in Go for your back-end exists, it should work out of the box.\n\nPlease note that the \"world view\" of existing tracing systems do differ.\nOpenTracing can not guarantee you that tracing alignment is perfect in a\nmicroservice environment especially one which is not exclusively OpenTracing\nenabled or switching from one tracing backend to another truly entails just a\nchange in configuration.\n\nThe following tracing back-ends are known to work with Go kit through the\nOpenTracing interface and are highlighted in the [addsvc] example.\n\n### AppDash\n\n[Appdash] support is available straight from their system repository in the\n[appdash/opentracing] directory.\n\n### LightStep\n\n[LightStep] support is available through their standard Go package\n[lightstep-tracer-go].\n\n### Zipkin\n\n[Zipkin] support is available through the [zipkin-go-opentracing] package.\n\n## OpenTelemetry\n\n[OpenTelemetry] came to life as a result of merging [OpenCensus] and [OpenTracing].\nGo kit instrumentation can be found in [opentelemetry-go-contrib]\nwhich is a central repository of instrumentation libraries.\n\n[Dapper]: http://research.google.com/pubs/pub36356.html\n[addsvc]: https://github.com/go-kit/examples/tree/master/addsvc\n[README]: https://github.com/go-kit/kit/blob/master/tracing/zipkin/README.md\n\n[OpenTracing]: http://opentracing.io\n[opentracing-go]: https://github.com/opentracing/opentracing-go\n\n[Zipkin]: http://zipkin.io/\n[Open Zipkin GitHub]: https://github.com/openzipkin\n[zipkin-go-opentracing]: https://github.com/openzipkin-contrib/zipkin-go-opentracing\n[zipkin-go]: https://github.com/openzipkin/zipkin-go\n[Zipkin's Gitter]: https://gitter.im/openzipkin/zipkin\n\n[Appdash]: https://github.com/sourcegraph/appdash\n[appdash/opentracing]: https://github.com/sourcegraph/appdash/tree/master/opentracing\n\n[LightStep]: http://lightstep.com/\n[lightstep-tracer-go]: https://github.com/lightstep/lightstep-tracer-go\n\n[OpenCensus]: https://opencensus.io/\n[opencensus-go]: https://github.com/census-instrumentation/opencensus-go\n\n[OpenTelemetry]: https://opentelemetry.io/\n[opentelemetry-go-contrib]: https://github.com/open-telemetry/opentelemetry-go-contrib\n"
  },
  {
    "path": "tracing/doc.go",
    "content": "// Package tracing provides helpers and bindings for distributed tracing.\n//\n// As your infrastructure grows, it becomes important to be able to trace a\n// request, as it travels through multiple services and back to the user.\n// Package tracing provides endpoints and transport helpers and middlewares to\n// capture and emit request-scoped information.\npackage tracing\n"
  },
  {
    "path": "tracing/opencensus/doc.go",
    "content": "// Package opencensus provides Go kit integration to the OpenCensus project.\n// OpenCensus is a single distribution of libraries for metrics and distributed\n// tracing with minimal overhead that allows you to export data to multiple\n// backends. The Go kit OpenCencus package as provided here contains middlewares\n// for tracing.\npackage opencensus\n"
  },
  {
    "path": "tracing/opencensus/endpoint.go",
    "content": "package opencensus\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\n\t\"go.opencensus.io/trace\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/sd/lb\"\n)\n\n// TraceEndpointDefaultName is the default endpoint span name to use.\nconst TraceEndpointDefaultName = \"gokit/endpoint\"\n\n// TraceEndpoint returns an Endpoint middleware, tracing a Go kit endpoint.\n// This endpoint tracer should be used in combination with a Go kit Transport\n// tracing middleware, generic OpenCensus transport middleware or custom before\n// and after transport functions as service propagation of SpanContext is not\n// provided in this middleware.\nfunc TraceEndpoint(name string, options ...EndpointOption) endpoint.Middleware {\n\tif name == \"\" {\n\t\tname = TraceEndpointDefaultName\n\t}\n\n\tcfg := &EndpointOptions{}\n\n\tfor _, o := range options {\n\t\to(cfg)\n\t}\n\n\treturn func(next endpoint.Endpoint) endpoint.Endpoint {\n\t\treturn func(ctx context.Context, request interface{}) (response interface{}, err error) {\n\t\t\tif cfg.GetName != nil {\n\t\t\t\tif newName := cfg.GetName(ctx, name); newName != \"\" {\n\t\t\t\t\tname = newName\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tctx, span := trace.StartSpan(ctx, name)\n\t\t\tif len(cfg.Attributes) > 0 {\n\t\t\t\tspan.AddAttributes(cfg.Attributes...)\n\t\t\t}\n\t\t\tdefer span.End()\n\n\t\t\tif cfg.GetAttributes != nil {\n\t\t\t\tif attrs := cfg.GetAttributes(ctx); len(attrs) > 0 {\n\t\t\t\t\tspan.AddAttributes(attrs...)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdefer func() {\n\t\t\t\tif err != nil {\n\t\t\t\t\tif lberr, ok := err.(lb.RetryError); ok {\n\t\t\t\t\t\t// handle errors originating from lb.Retry\n\t\t\t\t\t\tattrs := make([]trace.Attribute, 0, len(lberr.RawErrors))\n\t\t\t\t\t\tfor idx, rawErr := range lberr.RawErrors {\n\t\t\t\t\t\t\tattrs = append(attrs, trace.StringAttribute(\n\t\t\t\t\t\t\t\t\"gokit.retry.error.\"+strconv.Itoa(idx+1), rawErr.Error(),\n\t\t\t\t\t\t\t))\n\t\t\t\t\t\t}\n\t\t\t\t\t\tspan.AddAttributes(attrs...)\n\t\t\t\t\t\tspan.SetStatus(trace.Status{\n\t\t\t\t\t\t\tCode:    trace.StatusCodeUnknown,\n\t\t\t\t\t\t\tMessage: lberr.Final.Error(),\n\t\t\t\t\t\t})\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\t// generic error\n\t\t\t\t\tspan.SetStatus(trace.Status{\n\t\t\t\t\t\tCode:    trace.StatusCodeUnknown,\n\t\t\t\t\t\tMessage: err.Error(),\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// test for business error\n\t\t\t\tif res, ok := response.(endpoint.Failer); ok && res.Failed() != nil {\n\t\t\t\t\tspan.AddAttributes(\n\t\t\t\t\t\ttrace.StringAttribute(\"gokit.business.error\", res.Failed().Error()),\n\t\t\t\t\t)\n\t\t\t\t\tif cfg.IgnoreBusinessError {\n\t\t\t\t\t\tspan.SetStatus(trace.Status{Code: trace.StatusCodeOK})\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\t// treating business error as real error in span.\n\t\t\t\t\tspan.SetStatus(trace.Status{\n\t\t\t\t\t\tCode:    trace.StatusCodeUnknown,\n\t\t\t\t\t\tMessage: res.Failed().Error(),\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// no errors identified\n\t\t\t\tspan.SetStatus(trace.Status{Code: trace.StatusCodeOK})\n\t\t\t}()\n\t\t\tresponse, err = next(ctx, request)\n\t\t\treturn\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tracing/opencensus/endpoint_options.go",
    "content": "package opencensus\n\nimport (\n\t\"context\"\n\n\t\"go.opencensus.io/trace\"\n)\n\n// EndpointOptions holds the options for tracing an endpoint\ntype EndpointOptions struct {\n\t// IgnoreBusinessError if set to true will not treat a business error\n\t// identified through the endpoint.Failer interface as a span error.\n\tIgnoreBusinessError bool\n\n\t// Attributes holds the default attributes which will be set on span\n\t// creation by our Endpoint middleware.\n\tAttributes []trace.Attribute\n\n\t// GetName is an optional function that can set the span name based on the existing name\n\t// for the endpoint and information in the context.\n\t//\n\t// If the function is nil, or the returned name is empty, the existing name for the endpoint is used.\n\tGetName func(ctx context.Context, name string) string\n\n\t// GetAttributes is an optional function that can extract trace attributes \n\t// from the context and add them to the span.\n\tGetAttributes func(ctx context.Context) []trace.Attribute\n}\n\n// EndpointOption allows for functional options to our OpenCensus endpoint\n// tracing middleware.\ntype EndpointOption func(*EndpointOptions)\n\n// WithEndpointConfig sets all configuration options at once by use of the\n// EndpointOptions struct.\nfunc WithEndpointConfig(options EndpointOptions) EndpointOption {\n\treturn func(o *EndpointOptions) {\n\t\t*o = options\n\t}\n}\n\n// WithEndpointAttributes sets the default attributes for the spans created by\n// the Endpoint tracer.\nfunc WithEndpointAttributes(attrs ...trace.Attribute) EndpointOption {\n\treturn func(o *EndpointOptions) {\n\t\to.Attributes = attrs\n\t}\n}\n\n// WithIgnoreBusinessError if set to true will not treat a business error\n// identified through the endpoint.Failer interface as a span error.\nfunc WithIgnoreBusinessError(val bool) EndpointOption {\n\treturn func(o *EndpointOptions) {\n\t\to.IgnoreBusinessError = val\n\t}\n}\n\n// WithSpanName extracts additional attributes from the request context.\nfunc WithSpanName(fn func(ctx context.Context, name string) string) EndpointOption {\n\treturn func(o *EndpointOptions) {\n\t\to.GetName = fn\n\t}\n}\n\n// WithSpanAttributes extracts additional attributes from the request context.\nfunc WithSpanAttributes(fn func(ctx context.Context) []trace.Attribute) EndpointOption {\n\treturn func(o *EndpointOptions) {\n\t\to.GetAttributes = fn\n\t}\n}\n"
  },
  {
    "path": "tracing/opencensus/endpoint_test.go",
    "content": "package opencensus_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.opencensus.io/trace\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/kit/sd/lb\"\n\t\"github.com/go-kit/kit/tracing/opencensus\"\n)\n\nconst (\n\tspan1 = \"\"\n\tspan2 = \"SPAN-2\"\n\tspan3 = \"SPAN-3\"\n\tspan4 = \"SPAN-4\"\n\tspan5 = \"SPAN-5\"\n\tspan6 = \"SPAN-6\"\n)\n\nvar (\n\terr1 = errors.New(\"some error\")\n\terr2 = errors.New(\"other error\")\n\terr3 = errors.New(\"some business error\")\n\terr4 = errors.New(\"other business error\")\n)\n\n// compile time assertion\nvar _ endpoint.Failer = failedResponse{}\n\ntype failedResponse struct {\n\terr error\n}\n\nfunc (r failedResponse) Failed() error { return r.err }\n\nfunc passEndpoint(_ context.Context, req interface{}) (interface{}, error) {\n\tif err, _ := req.(error); err != nil {\n\t\treturn nil, err\n\t}\n\treturn req, nil\n}\n\nfunc TestTraceEndpoint(t *testing.T) {\n\tctx := context.Background()\n\n\te := &recordingExporter{}\n\ttrace.RegisterExporter(e)\n\ttrace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})\n\n\t// span 1\n\tspan1Attrs := []trace.Attribute{\n\t\ttrace.StringAttribute(\"string\", \"value\"),\n\t\ttrace.Int64Attribute(\"int64\", 42),\n\t}\n\tmw := opencensus.TraceEndpoint(\n\t\tspan1, opencensus.WithEndpointAttributes(span1Attrs...),\n\t)\n\tmw(endpoint.Nop)(ctx, nil)\n\n\t// span 2\n\topts := opencensus.EndpointOptions{}\n\tmw = opencensus.TraceEndpoint(span2, opencensus.WithEndpointConfig(opts))\n\tmw(passEndpoint)(ctx, err1)\n\n\t// span3\n\tmw = opencensus.TraceEndpoint(span3)\n\tep := lb.Retry(5, 1*time.Second, lb.NewRoundRobin(sd.FixedEndpointer{passEndpoint}))\n\tmw(ep)(ctx, err2)\n\n\t// span4\n\tmw = opencensus.TraceEndpoint(span4)\n\tmw(passEndpoint)(ctx, failedResponse{err: err3})\n\n\t// span5\n\tmw = opencensus.TraceEndpoint(span5, opencensus.WithIgnoreBusinessError(true))\n\tmw(passEndpoint)(ctx, failedResponse{err: err4})\n\n\t// span6\n\tspan6Attrs := []trace.Attribute{\n\t\ttrace.StringAttribute(\"string\", \"value\"),\n\t\ttrace.Int64Attribute(\"int64\", 42),\n\t}\n\tmw = opencensus.TraceEndpoint(\n\t\t\"\",\n\t\topencensus.WithSpanName(func(ctx context.Context, name string) string {\n\t\t\treturn span6\n\t\t}),\n\t\topencensus.WithSpanAttributes(func(ctx context.Context) []trace.Attribute {\n\t\t\treturn span6Attrs\n\t\t}),\n\t)\n\tmw(endpoint.Nop)(ctx, nil)\n\n\t// check span count\n\tspans := e.Flush()\n\tif want, have := 6, len(spans); want != have {\n\t\tt.Fatalf(\"incorrected number of spans, wanted %d, got %d\", want, have)\n\t}\n\n\t// test span 1\n\tspan := spans[0]\n\tif want, have := int32(trace.StatusCodeOK), span.Code; want != have {\n\t\tt.Errorf(\"incorrect status code, wanted %d, got %d\", want, have)\n\t}\n\n\tif want, have := opencensus.TraceEndpointDefaultName, span.Name; want != have {\n\t\tt.Errorf(\"incorrect span name, wanted %q, got %q\", want, have)\n\t}\n\n\tif want, have := 2, len(span.Attributes); want != have {\n\t\tt.Fatalf(\"incorrect attribute count, wanted %d, got %d\", want, have)\n\t}\n\n\t// test span 2\n\tspan = spans[1]\n\tif want, have := int32(trace.StatusCodeUnknown), span.Code; want != have {\n\t\tt.Errorf(\"incorrect status code, wanted %d, got %d\", want, have)\n\t}\n\n\tif want, have := span2, span.Name; want != have {\n\t\tt.Errorf(\"incorrect span name, wanted %q, got %q\", want, have)\n\t}\n\n\tif want, have := 0, len(span.Attributes); want != have {\n\t\tt.Fatalf(\"incorrect attribute count, wanted %d, got %d\", want, have)\n\t}\n\n\t// test span 3\n\tspan = spans[2]\n\tif want, have := int32(trace.StatusCodeUnknown), span.Code; want != have {\n\t\tt.Errorf(\"incorrect status code, wanted %d, got %d\", want, have)\n\t}\n\n\tif want, have := span3, span.Name; want != have {\n\t\tt.Errorf(\"incorrect span name, wanted %q, got %q\", want, have)\n\t}\n\n\tif want, have := 5, len(span.Attributes); want != have {\n\t\tt.Fatalf(\"incorrect attribute count, wanted %d, got %d\", want, have)\n\t}\n\n\t// test span 4\n\tspan = spans[3]\n\tif want, have := int32(trace.StatusCodeUnknown), span.Code; want != have {\n\t\tt.Errorf(\"incorrect status code, wanted %d, got %d\", want, have)\n\t}\n\n\tif want, have := span4, span.Name; want != have {\n\t\tt.Errorf(\"incorrect span name, wanted %q, got %q\", want, have)\n\t}\n\n\tif want, have := 1, len(span.Attributes); want != have {\n\t\tt.Fatalf(\"incorrect attribute count, wanted %d, got %d\", want, have)\n\t}\n\n\t// test span 5\n\tspan = spans[4]\n\tif want, have := int32(trace.StatusCodeOK), span.Code; want != have {\n\t\tt.Errorf(\"incorrect status code, wanted %d, got %d\", want, have)\n\t}\n\n\tif want, have := span5, span.Name; want != have {\n\t\tt.Errorf(\"incorrect span name, wanted %q, got %q\", want, have)\n\t}\n\n\tif want, have := 1, len(span.Attributes); want != have {\n\t\tt.Fatalf(\"incorrect attribute count, wanted %d, got %d\", want, have)\n\t}\n\n\t// test span 6\n\tspan = spans[5]\n\tif want, have := span6, span.Name; want != have {\n\t\tt.Errorf(\"incorrect span name, wanted %q, got %q\", want, have)\n\t}\n\n\tif want, have := 2, len(span.Attributes); want != have {\n\t\tt.Fatalf(\"incorrect attribute count, wanted %d, got %d\", want, have)\n\t}\n}\n"
  },
  {
    "path": "tracing/opencensus/grpc.go",
    "content": "package opencensus\n\nimport (\n\t\"context\"\n\n\t\"go.opencensus.io/trace\"\n\t\"go.opencensus.io/trace/propagation\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/metadata\"\n\t\"google.golang.org/grpc/status\"\n\n\tkitgrpc \"github.com/go-kit/kit/transport/grpc\"\n)\n\nconst propagationKey = \"grpc-trace-bin\"\n\n// GRPCClientTrace enables OpenCensus tracing of a Go kit gRPC transport client.\nfunc GRPCClientTrace(options ...TracerOption) kitgrpc.ClientOption {\n\tcfg := TracerOptions{}\n\n\tfor _, option := range options {\n\t\toption(&cfg)\n\t}\n\n\tclientBefore := kitgrpc.ClientBefore(\n\t\tfunc(ctx context.Context, md *metadata.MD) context.Context {\n\t\t\tvar name string\n\n\t\t\tif cfg.Name != \"\" {\n\t\t\t\tname = cfg.Name\n\t\t\t} else {\n\t\t\t\tname = ctx.Value(kitgrpc.ContextKeyRequestMethod).(string)\n\t\t\t}\n\n\t\t\tctx, span := trace.StartSpan(\n\t\t\t\tctx,\n\t\t\t\tname,\n\t\t\t\ttrace.WithSampler(cfg.Sampler),\n\t\t\t\ttrace.WithSpanKind(trace.SpanKindClient),\n\t\t\t)\n\n\t\t\tif !cfg.Public {\n\t\t\t\ttraceContextBinary := string(propagation.Binary(span.SpanContext()))\n\t\t\t\t(*md)[propagationKey] = append((*md)[propagationKey], traceContextBinary)\n\t\t\t}\n\n\t\t\treturn ctx\n\t\t},\n\t)\n\n\tclientFinalizer := kitgrpc.ClientFinalizer(\n\t\tfunc(ctx context.Context, err error) {\n\t\t\tif span := trace.FromContext(ctx); span != nil {\n\t\t\t\tif s, ok := status.FromError(err); ok {\n\t\t\t\t\tspan.SetStatus(trace.Status{Code: int32(s.Code()), Message: s.Message()})\n\t\t\t\t} else {\n\t\t\t\t\tspan.SetStatus(trace.Status{Code: int32(codes.Unknown), Message: err.Error()})\n\t\t\t\t}\n\t\t\t\tspan.End()\n\t\t\t}\n\t\t},\n\t)\n\n\treturn func(c *kitgrpc.Client) {\n\t\tclientBefore(c)\n\t\tclientFinalizer(c)\n\t}\n}\n\n// GRPCServerTrace enables OpenCensus tracing of a Go kit gRPC transport server.\nfunc GRPCServerTrace(options ...TracerOption) kitgrpc.ServerOption {\n\tcfg := TracerOptions{}\n\n\tfor _, option := range options {\n\t\toption(&cfg)\n\t}\n\n\tserverBefore := kitgrpc.ServerBefore(\n\t\tfunc(ctx context.Context, md metadata.MD) context.Context {\n\t\t\tvar name string\n\n\t\t\tif cfg.Name != \"\" {\n\t\t\t\tname = cfg.Name\n\t\t\t} else {\n\t\t\t\tname, _ = ctx.Value(kitgrpc.ContextKeyRequestMethod).(string)\n\t\t\t\tif name == \"\" {\n\t\t\t\t\t// we can't find the gRPC method. probably the\n\t\t\t\t\t// unaryInterceptor was not wired up.\n\t\t\t\t\tname = \"unknown grpc method\"\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvar (\n\t\t\t\tparentContext trace.SpanContext\n\t\t\t\ttraceContext  = md[propagationKey]\n\t\t\t\tok            bool\n\t\t\t)\n\n\t\t\tif len(traceContext) > 0 {\n\t\t\t\ttraceContextBinary := []byte(traceContext[0])\n\t\t\t\tparentContext, ok = propagation.FromBinary(traceContextBinary)\n\t\t\t\tif ok && !cfg.Public {\n\t\t\t\t\tctx, _ = trace.StartSpanWithRemoteParent(\n\t\t\t\t\t\tctx,\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tparentContext,\n\t\t\t\t\t\ttrace.WithSpanKind(trace.SpanKindServer),\n\t\t\t\t\t\ttrace.WithSampler(cfg.Sampler),\n\t\t\t\t\t)\n\t\t\t\t\treturn ctx\n\t\t\t\t}\n\t\t\t}\n\t\t\tctx, span := trace.StartSpan(\n\t\t\t\tctx,\n\t\t\t\tname,\n\t\t\t\ttrace.WithSpanKind(trace.SpanKindServer),\n\t\t\t\ttrace.WithSampler(cfg.Sampler),\n\t\t\t)\n\t\t\tif ok {\n\t\t\t\tspan.AddLink(\n\t\t\t\t\ttrace.Link{\n\t\t\t\t\t\tTraceID: parentContext.TraceID,\n\t\t\t\t\t\tSpanID:  parentContext.SpanID,\n\t\t\t\t\t\tType:    trace.LinkTypeChild,\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn ctx\n\t\t},\n\t)\n\n\tserverFinalizer := kitgrpc.ServerFinalizer(\n\t\tfunc(ctx context.Context, err error) {\n\t\t\tif span := trace.FromContext(ctx); span != nil {\n\t\t\t\tif s, ok := status.FromError(err); ok {\n\t\t\t\t\tspan.SetStatus(trace.Status{Code: int32(s.Code()), Message: s.Message()})\n\t\t\t\t} else {\n\t\t\t\t\tspan.SetStatus(trace.Status{Code: int32(codes.Internal), Message: err.Error()})\n\t\t\t\t}\n\t\t\t\tspan.End()\n\t\t\t}\n\t\t},\n\t)\n\n\treturn func(s *kitgrpc.Server) {\n\t\tserverBefore(s)\n\t\tserverFinalizer(s)\n\t}\n}\n"
  },
  {
    "path": "tracing/opencensus/grpc_test.go",
    "content": "package opencensus_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"go.opencensus.io/trace\"\n\t\"go.opencensus.io/trace/propagation\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/metadata\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\tockit \"github.com/go-kit/kit/tracing/opencensus\"\n\tgrpctransport \"github.com/go-kit/kit/transport/grpc\"\n)\n\ntype dummy struct{}\n\nconst traceContextKey = \"grpc-trace-bin\"\n\nfunc unaryInterceptor(\n\tctx context.Context, method string, req, reply interface{},\n\tcc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption,\n) error {\n\treturn nil\n}\n\nfunc TestGRPCClientTrace(t *testing.T) {\n\trec := &recordingExporter{}\n\n\ttrace.RegisterExporter(rec)\n\n\tcc, err := grpc.Dial(\n\t\t\"\",\n\t\tgrpc.WithUnaryInterceptor(unaryInterceptor),\n\t\tgrpc.WithInsecure(),\n\t)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to create gRPC dialer: %s\", err.Error())\n\t}\n\n\ttraces := []struct {\n\t\tname string\n\t\terr  error\n\t}{\n\t\t{\"\", nil},\n\t\t{\"CustomName\", nil},\n\t\t{\"\", errors.New(\"dummy-error\")},\n\t}\n\n\tfor _, tr := range traces {\n\t\tclientTracer := ockit.GRPCClientTrace(\n\t\t\tockit.WithName(tr.name),\n\t\t\tockit.WithSampler(trace.AlwaysSample()),\n\t\t)\n\n\t\tep := grpctransport.NewClient(\n\t\t\tcc,\n\t\t\t\"dummyService\",\n\t\t\t\"dummyMethod\",\n\t\t\tfunc(context.Context, interface{}) (interface{}, error) {\n\t\t\t\treturn nil, nil\n\t\t\t},\n\t\t\tfunc(context.Context, interface{}) (interface{}, error) {\n\t\t\t\treturn nil, tr.err\n\t\t\t},\n\t\t\tdummy{},\n\t\t\tclientTracer,\n\t\t).Endpoint()\n\n\t\tctx, parentSpan := trace.StartSpan(context.Background(), \"test\")\n\n\t\t_, err = ep(ctx, nil)\n\t\tif want, have := tr.err, err; want != have {\n\t\t\tt.Fatalf(\"unexpected error, want %s, have %s\", tr.err.Error(), err.Error())\n\t\t}\n\n\t\tspans := rec.Flush()\n\t\tif want, have := 1, len(spans); want != have {\n\t\t\tt.Fatalf(\"incorrect number of spans, want %d, have %d\", want, have)\n\t\t}\n\t\tspan := spans[0]\n\t\tif want, have := parentSpan.SpanContext().SpanID, span.ParentSpanID; want != have {\n\t\t\tt.Errorf(\"incorrect parent ID, want %s, have %s\", want, have)\n\t\t}\n\n\t\tif want, have := tr.name, span.Name; want != have && want != \"\" {\n\t\t\tt.Errorf(\"incorrect span name, want %s, have %s\", want, have)\n\t\t}\n\n\t\tif want, have := \"/dummyService/dummyMethod\", span.Name; want != have && tr.name == \"\" {\n\t\t\tt.Errorf(\"incorrect span name, want %s, have %s\", want, have)\n\t\t}\n\n\t\tcode := trace.StatusCodeOK\n\t\tif tr.err != nil {\n\t\t\tcode = trace.StatusCodeUnknown\n\n\t\t\tif want, have := err.Error(), span.Status.Message; want != have {\n\t\t\t\tt.Errorf(\"incorrect span status msg, want %s, have %s\", want, have)\n\t\t\t}\n\t\t}\n\n\t\tif want, have := int32(code), span.Status.Code; want != have {\n\t\t\tt.Errorf(\"incorrect span status code, want %d, have %d\", want, have)\n\t\t}\n\t}\n}\n\nfunc TestGRPCServerTrace(t *testing.T) {\n\trec := &recordingExporter{}\n\n\ttrace.RegisterExporter(rec)\n\n\ttraces := []struct {\n\t\tuseParent bool\n\t\tname      string\n\t\terr       error\n\t}{\n\t\t{false, \"\", nil},\n\t\t{true, \"\", nil},\n\t\t{true, \"CustomName\", nil},\n\t\t{true, \"\", errors.New(\"dummy-error\")},\n\t}\n\n\tfor _, tr := range traces {\n\t\tvar (\n\t\t\tctx        = context.Background()\n\t\t\tparentSpan *trace.Span\n\t\t)\n\n\t\tserver := grpctransport.NewServer(\n\t\t\tendpoint.Nop,\n\t\t\tfunc(context.Context, interface{}) (interface{}, error) {\n\t\t\t\treturn nil, nil\n\t\t\t},\n\t\t\tfunc(context.Context, interface{}) (interface{}, error) {\n\t\t\t\treturn nil, tr.err\n\t\t\t},\n\t\t\tockit.GRPCServerTrace(\n\t\t\t\tockit.WithName(tr.name),\n\t\t\t\tockit.WithSampler(trace.AlwaysSample()),\n\t\t\t),\n\t\t)\n\n\t\tif tr.useParent {\n\t\t\t_, parentSpan = trace.StartSpan(context.Background(), \"test\")\n\t\t\ttraceContextBinary := propagation.Binary(parentSpan.SpanContext())\n\n\t\t\tmd := metadata.MD{}\n\t\t\tmd.Set(traceContextKey, string(traceContextBinary))\n\t\t\tctx = metadata.NewIncomingContext(ctx, md)\n\t\t}\n\n\t\tserver.ServeGRPC(ctx, nil)\n\n\t\tspans := rec.Flush()\n\n\t\tif want, have := 1, len(spans); want != have {\n\t\t\tt.Fatalf(\"incorrect number of spans, want %d, have %d\", want, have)\n\t\t}\n\n\t\tif tr.useParent {\n\t\t\tif want, have := parentSpan.SpanContext().TraceID, spans[0].TraceID; want != have {\n\t\t\t\tt.Errorf(\"incorrect trace ID, want %s, have %s\", want, have)\n\t\t\t}\n\n\t\t\tif want, have := parentSpan.SpanContext().SpanID, spans[0].ParentSpanID; want != have {\n\t\t\t\tt.Errorf(\"incorrect span ID, want %s, have %s\", want, have)\n\t\t\t}\n\t\t}\n\n\t\tif want, have := tr.name, spans[0].Name; want != have && want != \"\" {\n\t\t\tt.Errorf(\"incorrect span name, want %s, have %s\", want, have)\n\t\t}\n\n\t\tif tr.err != nil {\n\t\t\tif want, have := int32(codes.Internal), spans[0].Status.Code; want != have {\n\t\t\t\tt.Errorf(\"incorrect span status code, want %d, have %d\", want, have)\n\t\t\t}\n\n\t\t\tif want, have := tr.err.Error(), spans[0].Status.Message; want != have {\n\t\t\t\tt.Errorf(\"incorrect span status message, want %s, have %s\", want, have)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tracing/opencensus/http.go",
    "content": "package opencensus\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"go.opencensus.io/plugin/ochttp\"\n\t\"go.opencensus.io/plugin/ochttp/propagation/b3\"\n\t\"go.opencensus.io/trace\"\n\n\tkithttp \"github.com/go-kit/kit/transport/http\"\n)\n\n// HTTPClientTrace enables OpenCensus tracing of a Go kit HTTP transport client.\nfunc HTTPClientTrace(options ...TracerOption) kithttp.ClientOption {\n\tcfg := TracerOptions{}\n\n\tfor _, option := range options {\n\t\toption(&cfg)\n\t}\n\n\tif !cfg.Public && cfg.HTTPPropagate == nil {\n\t\tcfg.HTTPPropagate = &b3.HTTPFormat{}\n\t}\n\n\tclientBefore := kithttp.ClientBefore(\n\t\tfunc(ctx context.Context, req *http.Request) context.Context {\n\t\t\tvar name string\n\n\t\t\tif cfg.Name != \"\" {\n\t\t\t\tname = cfg.Name\n\t\t\t} else {\n\t\t\t\t// OpenCensus states Path being default naming for a client span\n\t\t\t\tname = req.Method + \" \" + req.URL.Path\n\t\t\t}\n\n\t\t\tctx, span := trace.StartSpan(\n\t\t\t\tctx,\n\t\t\t\tname,\n\t\t\t\ttrace.WithSampler(cfg.Sampler),\n\t\t\t\ttrace.WithSpanKind(trace.SpanKindClient),\n\t\t\t)\n\n\t\t\tspan.AddAttributes(\n\t\t\t\ttrace.StringAttribute(ochttp.HostAttribute, req.URL.Host),\n\t\t\t\ttrace.StringAttribute(ochttp.MethodAttribute, req.Method),\n\t\t\t\ttrace.StringAttribute(ochttp.PathAttribute, req.URL.Path),\n\t\t\t\ttrace.StringAttribute(ochttp.UserAgentAttribute, req.UserAgent()),\n\t\t\t)\n\n\t\t\tif !cfg.Public {\n\t\t\t\tcfg.HTTPPropagate.SpanContextToRequest(span.SpanContext(), req)\n\t\t\t}\n\n\t\t\treturn ctx\n\t\t},\n\t)\n\n\tclientAfter := kithttp.ClientAfter(\n\t\tfunc(ctx context.Context, res *http.Response) context.Context {\n\t\t\tif span := trace.FromContext(ctx); span != nil {\n\t\t\t\tspan.SetStatus(ochttp.TraceStatus(res.StatusCode, http.StatusText(res.StatusCode)))\n\t\t\t\tspan.AddAttributes(\n\t\t\t\t\ttrace.Int64Attribute(ochttp.StatusCodeAttribute, int64(res.StatusCode)),\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn ctx\n\t\t},\n\t)\n\n\tclientFinalizer := kithttp.ClientFinalizer(\n\t\tfunc(ctx context.Context, err error) {\n\t\t\tif span := trace.FromContext(ctx); span != nil {\n\t\t\t\tif err != nil {\n\t\t\t\t\tspan.SetStatus(trace.Status{\n\t\t\t\t\t\tCode:    trace.StatusCodeUnknown,\n\t\t\t\t\t\tMessage: err.Error(),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tspan.End()\n\t\t\t}\n\t\t},\n\t)\n\n\treturn func(c *kithttp.Client) {\n\t\tclientBefore(c)\n\t\tclientAfter(c)\n\t\tclientFinalizer(c)\n\t}\n}\n\n// HTTPServerTrace enables OpenCensus tracing of a Go kit HTTP transport server.\nfunc HTTPServerTrace(options ...TracerOption) kithttp.ServerOption {\n\tcfg := TracerOptions{}\n\n\tfor _, option := range options {\n\t\toption(&cfg)\n\t}\n\n\tif !cfg.Public && cfg.HTTPPropagate == nil {\n\t\tcfg.HTTPPropagate = &b3.HTTPFormat{}\n\t}\n\n\tserverBefore := kithttp.ServerBefore(\n\t\tfunc(ctx context.Context, req *http.Request) context.Context {\n\t\t\tvar (\n\t\t\t\tspanContext trace.SpanContext\n\t\t\t\tspan        *trace.Span\n\t\t\t\tname        string\n\t\t\t\tok          bool\n\t\t\t)\n\n\t\t\tif cfg.Name != \"\" {\n\t\t\t\tname = cfg.Name\n\t\t\t} else {\n\t\t\t\tname = req.Method + \" \" + req.URL.Path\n\t\t\t}\n\n\t\t\tspanContext, ok = cfg.HTTPPropagate.SpanContextFromRequest(req)\n\t\t\tif ok && !cfg.Public {\n\t\t\t\tctx, span = trace.StartSpanWithRemoteParent(\n\t\t\t\t\tctx,\n\t\t\t\t\tname,\n\t\t\t\t\tspanContext,\n\t\t\t\t\ttrace.WithSpanKind(trace.SpanKindServer),\n\t\t\t\t\ttrace.WithSampler(cfg.Sampler),\n\t\t\t\t)\n\t\t\t} else {\n\t\t\t\tctx, span = trace.StartSpan(\n\t\t\t\t\tctx,\n\t\t\t\t\tname,\n\t\t\t\t\ttrace.WithSpanKind(trace.SpanKindServer),\n\t\t\t\t\ttrace.WithSampler(cfg.Sampler),\n\t\t\t\t)\n\t\t\t\tif ok {\n\t\t\t\t\tspan.AddLink(trace.Link{\n\t\t\t\t\t\tTraceID:    spanContext.TraceID,\n\t\t\t\t\t\tSpanID:     spanContext.SpanID,\n\t\t\t\t\t\tType:       trace.LinkTypeChild,\n\t\t\t\t\t\tAttributes: nil,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tspan.AddAttributes(\n\t\t\t\ttrace.StringAttribute(ochttp.MethodAttribute, req.Method),\n\t\t\t\ttrace.StringAttribute(ochttp.PathAttribute, req.URL.Path),\n\t\t\t)\n\n\t\t\treturn ctx\n\t\t},\n\t)\n\n\tserverFinalizer := kithttp.ServerFinalizer(\n\t\tfunc(ctx context.Context, code int, r *http.Request) {\n\t\t\tif span := trace.FromContext(ctx); span != nil {\n\t\t\t\tspan.SetStatus(ochttp.TraceStatus(code, http.StatusText(code)))\n\n\t\t\t\tif rs, ok := ctx.Value(kithttp.ContextKeyResponseSize).(int64); ok {\n\t\t\t\t\tspan.AddAttributes(\n\t\t\t\t\t\ttrace.Int64Attribute(\"http.response_size\", rs),\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\tspan.End()\n\t\t\t}\n\t\t},\n\t)\n\n\treturn func(s *kithttp.Server) {\n\t\tserverBefore(s)\n\t\tserverFinalizer(s)\n\t}\n}\n"
  },
  {
    "path": "tracing/opencensus/http_test.go",
    "content": "package opencensus_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"go.opencensus.io/plugin/ochttp\"\n\t\"go.opencensus.io/plugin/ochttp/propagation/b3\"\n\t\"go.opencensus.io/plugin/ochttp/propagation/tracecontext\"\n\t\"go.opencensus.io/trace\"\n\t\"go.opencensus.io/trace/propagation\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\tockit \"github.com/go-kit/kit/tracing/opencensus\"\n\tkithttp \"github.com/go-kit/kit/transport/http\"\n)\n\nfunc TestHTTPClientTrace(t *testing.T) {\n\tvar (\n\t\terr     error\n\t\trec     = &recordingExporter{}\n\t\trURL, _ = url.Parse(\"https://httpbin.org/get\")\n\t)\n\n\ttrace.RegisterExporter(rec)\n\n\ttraces := []struct {\n\t\tname string\n\t\terr  error\n\t}{\n\t\t{\"\", nil},\n\t\t{\"CustomName\", nil},\n\t\t{\"\", errors.New(\"dummy-error\")},\n\t}\n\n\tfor _, tr := range traces {\n\t\tclientTracer := ockit.HTTPClientTrace(\n\t\t\tockit.WithName(tr.name),\n\t\t\tockit.WithSampler(trace.AlwaysSample()),\n\t\t)\n\t\tep := kithttp.NewClient(\n\t\t\t\"GET\",\n\t\t\trURL,\n\t\t\tfunc(ctx context.Context, r *http.Request, i interface{}) error {\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tfunc(ctx context.Context, r *http.Response) (response interface{}, err error) {\n\t\t\t\treturn nil, tr.err\n\t\t\t},\n\t\t\tclientTracer,\n\t\t).Endpoint()\n\n\t\tctx, parentSpan := trace.StartSpan(context.Background(), \"test\")\n\n\t\t_, err = ep(ctx, nil)\n\t\tif want, have := tr.err, err; want != have {\n\t\t\tt.Fatalf(\"unexpected error, want %s, have %s\", tr.err.Error(), err.Error())\n\t\t}\n\n\t\tspans := rec.Flush()\n\t\tif want, have := 1, len(spans); want != have {\n\t\t\tt.Fatalf(\"incorrect number of spans, want %d, have %d\", want, have)\n\t\t}\n\n\t\tspan := spans[0]\n\t\tif want, have := parentSpan.SpanContext().SpanID, span.ParentSpanID; want != have {\n\t\t\tt.Errorf(\"incorrect parent ID, want %s, have %s\", want, have)\n\t\t}\n\n\t\tif want, have := tr.name, span.Name; want != have && want != \"\" {\n\t\t\tt.Errorf(\"incorrect span name, want %s, have %s\", want, have)\n\t\t}\n\n\t\tif want, have := \"GET /get\", span.Name; want != have && tr.name == \"\" {\n\t\t\tt.Errorf(\"incorrect span name, want %s, have %s\", want, have)\n\t\t}\n\n\t\tcode := trace.StatusCodeOK\n\t\tif tr.err != nil {\n\t\t\tcode = trace.StatusCodeUnknown\n\n\t\t\tif want, have := err.Error(), span.Status.Message; want != have {\n\t\t\t\tt.Errorf(\"incorrect span status msg, want %s, have %s\", want, have)\n\t\t\t}\n\t\t}\n\n\t\tif want, have := int32(code), span.Status.Code; want != have {\n\t\t\tt.Errorf(\"incorrect span status code, want %d, have %d\", want, have)\n\t\t}\n\t}\n}\n\nfunc TestHTTPServerTrace(t *testing.T) {\n\trec := &recordingExporter{}\n\n\ttrace.RegisterExporter(rec)\n\n\ttraces := []struct {\n\t\tuseParent   bool\n\t\tname        string\n\t\terr         error\n\t\tpropagation propagation.HTTPFormat\n\t}{\n\t\t{false, \"\", nil, nil},\n\t\t{true, \"\", nil, nil},\n\t\t{true, \"CustomName\", nil, &b3.HTTPFormat{}},\n\t\t{true, \"\", errors.New(\"dummy-error\"), &tracecontext.HTTPFormat{}},\n\t}\n\n\tfor _, tr := range traces {\n\t\tvar client http.Client\n\n\t\thandler := kithttp.NewServer(\n\t\t\tendpoint.Nop,\n\t\t\tfunc(context.Context, *http.Request) (interface{}, error) { return nil, nil },\n\t\t\tfunc(context.Context, http.ResponseWriter, interface{}) error { return errors.New(\"dummy\") },\n\t\t\tockit.HTTPServerTrace(\n\t\t\t\tockit.WithName(tr.name),\n\t\t\t\tockit.WithSampler(trace.AlwaysSample()),\n\t\t\t\tockit.WithHTTPPropagation(tr.propagation),\n\t\t\t),\n\t\t)\n\n\t\tserver := httptest.NewServer(handler)\n\t\tdefer server.Close()\n\n\t\tconst httpMethod = \"GET\"\n\n\t\treq, err := http.NewRequest(httpMethod, server.URL, nil)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"unable to create HTTP request: %s\", err.Error())\n\t\t}\n\n\t\tif tr.useParent {\n\t\t\tclient = http.Client{\n\t\t\t\tTransport: &ochttp.Transport{\n\t\t\t\t\tStartOptions: trace.StartOptions{\n\t\t\t\t\t\tSampler: trace.AlwaysSample(),\n\t\t\t\t\t},\n\t\t\t\t\tPropagation: tr.propagation,\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\n\t\tresp, err := client.Do(req.WithContext(context.Background()))\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"unable to send HTTP request: %s\", err.Error())\n\t\t}\n\t\tresp.Body.Close()\n\n\t\tspans := rec.Flush()\n\n\t\texpectedSpans := 1\n\t\tif tr.useParent {\n\t\t\texpectedSpans++\n\t\t}\n\n\t\tif want, have := expectedSpans, len(spans); want != have {\n\t\t\tt.Fatalf(\"incorrect number of spans, want %d, have %d\", want, have)\n\t\t}\n\n\t\tif tr.useParent {\n\t\t\tif want, have := spans[1].TraceID, spans[0].TraceID; want != have {\n\t\t\t\tt.Errorf(\"incorrect trace ID, want %s, have %s\", want, have)\n\t\t\t}\n\n\t\t\tif want, have := spans[1].SpanID, spans[0].ParentSpanID; want != have {\n\t\t\t\tt.Errorf(\"incorrect span ID, want %s, have %s\", want, have)\n\t\t\t}\n\t\t}\n\n\t\tif want, have := tr.name, spans[0].Name; want != have && want != \"\" {\n\t\t\tt.Errorf(\"incorrect span name, want %s, have %s\", want, have)\n\t\t}\n\n\t\tif want, have := \"GET /\", spans[0].Name; want != have && tr.name == \"\" {\n\t\t\tt.Errorf(\"incorrect span name, want %s, have %s\", want, have)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tracing/opencensus/jsonrpc.go",
    "content": "package opencensus\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"go.opencensus.io/plugin/ochttp\"\n\t\"go.opencensus.io/plugin/ochttp/propagation/b3\"\n\t\"go.opencensus.io/trace\"\n\n\tkithttp \"github.com/go-kit/kit/transport/http\"\n\tjsonrpc \"github.com/go-kit/kit/transport/http/jsonrpc\"\n)\n\n// JSONRPCClientTrace enables OpenCensus tracing of a Go kit JSONRPC transport client.\nfunc JSONRPCClientTrace(options ...TracerOption) jsonrpc.ClientOption {\n\tcfg := TracerOptions{}\n\n\tfor _, option := range options {\n\t\toption(&cfg)\n\t}\n\n\tif !cfg.Public && cfg.HTTPPropagate == nil {\n\t\tcfg.HTTPPropagate = &b3.HTTPFormat{}\n\t}\n\n\tclientBefore := jsonrpc.ClientBefore(\n\t\tfunc(ctx context.Context, req *http.Request) context.Context {\n\t\t\tvar name string\n\n\t\t\tif cfg.Name != \"\" {\n\t\t\t\tname = cfg.Name\n\t\t\t} else {\n\t\t\t\t// OpenCensus states Path being default naming for a client span\n\t\t\t\tname = ctx.Value(jsonrpc.ContextKeyRequestMethod).(string)\n\t\t\t}\n\n\t\t\tctx, span := trace.StartSpan(\n\t\t\t\tctx,\n\t\t\t\tname,\n\t\t\t\ttrace.WithSampler(cfg.Sampler),\n\t\t\t\ttrace.WithSpanKind(trace.SpanKindClient),\n\t\t\t)\n\n\t\t\tspan.AddAttributes(\n\t\t\t\ttrace.StringAttribute(ochttp.HostAttribute, req.URL.Host),\n\t\t\t\ttrace.StringAttribute(ochttp.MethodAttribute, req.Method),\n\t\t\t\ttrace.StringAttribute(ochttp.PathAttribute, req.URL.Path),\n\t\t\t\ttrace.StringAttribute(ochttp.UserAgentAttribute, req.UserAgent()),\n\t\t\t)\n\n\t\t\tif !cfg.Public {\n\t\t\t\tcfg.HTTPPropagate.SpanContextToRequest(span.SpanContext(), req)\n\t\t\t}\n\n\t\t\treturn ctx\n\t\t},\n\t)\n\n\tclientAfter := jsonrpc.ClientAfter(\n\t\tfunc(ctx context.Context, res *http.Response) context.Context {\n\t\t\tif span := trace.FromContext(ctx); span != nil {\n\t\t\t\tspan.SetStatus(ochttp.TraceStatus(res.StatusCode, http.StatusText(res.StatusCode)))\n\t\t\t\tspan.AddAttributes(\n\t\t\t\t\ttrace.Int64Attribute(ochttp.StatusCodeAttribute, int64(res.StatusCode)),\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn ctx\n\t\t},\n\t)\n\n\tclientFinalizer := jsonrpc.ClientFinalizer(\n\t\tfunc(ctx context.Context, err error) {\n\t\t\tif span := trace.FromContext(ctx); span != nil {\n\t\t\t\tif err != nil {\n\t\t\t\t\tspan.SetStatus(trace.Status{\n\t\t\t\t\t\tCode:    trace.StatusCodeUnknown,\n\t\t\t\t\t\tMessage: err.Error(),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tspan.End()\n\t\t\t}\n\t\t},\n\t)\n\n\treturn func(c *jsonrpc.Client) {\n\t\tclientBefore(c)\n\t\tclientAfter(c)\n\t\tclientFinalizer(c)\n\t}\n}\n\n// JSONRPCServerTrace enables OpenCensus tracing of a Go kit JSONRPC transport server.\nfunc JSONRPCServerTrace(options ...TracerOption) jsonrpc.ServerOption {\n\tcfg := TracerOptions{}\n\n\tfor _, option := range options {\n\t\toption(&cfg)\n\t}\n\n\tif !cfg.Public && cfg.HTTPPropagate == nil {\n\t\tcfg.HTTPPropagate = &b3.HTTPFormat{}\n\t}\n\n\tserverBeforeCodec := jsonrpc.ServerBeforeCodec(\n\t\tfunc(ctx context.Context, httpReq *http.Request, req jsonrpc.Request) context.Context {\n\t\t\tvar (\n\t\t\t\tspanContext trace.SpanContext\n\t\t\t\tspan        *trace.Span\n\t\t\t\tname        string\n\t\t\t\tok          bool\n\t\t\t)\n\n\t\t\tif cfg.Name != \"\" {\n\t\t\t\tname = cfg.Name\n\t\t\t} else {\n\t\t\t\tname = ctx.Value(jsonrpc.ContextKeyRequestMethod).(string)\n\t\t\t\tif name == \"\" {\n\t\t\t\t\t// we can't find the rpc method. probably the\n\t\t\t\t\t// unaryInterceptor was not wired up.\n\t\t\t\t\tname = \"unknown jsonrpc method\"\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tspanContext, ok = cfg.HTTPPropagate.SpanContextFromRequest(httpReq)\n\t\t\tif ok && !cfg.Public {\n\t\t\t\tctx, span = trace.StartSpanWithRemoteParent(\n\t\t\t\t\tctx,\n\t\t\t\t\tname,\n\t\t\t\t\tspanContext,\n\t\t\t\t\ttrace.WithSpanKind(trace.SpanKindServer),\n\t\t\t\t\ttrace.WithSampler(cfg.Sampler),\n\t\t\t\t)\n\t\t\t} else {\n\t\t\t\tctx, span = trace.StartSpan(\n\t\t\t\t\tctx,\n\t\t\t\t\tname,\n\t\t\t\t\ttrace.WithSpanKind(trace.SpanKindServer),\n\t\t\t\t\ttrace.WithSampler(cfg.Sampler),\n\t\t\t\t)\n\t\t\t\tif ok {\n\t\t\t\t\tspan.AddLink(trace.Link{\n\t\t\t\t\t\tTraceID:    spanContext.TraceID,\n\t\t\t\t\t\tSpanID:     spanContext.SpanID,\n\t\t\t\t\t\tType:       trace.LinkTypeChild,\n\t\t\t\t\t\tAttributes: nil,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tspan.AddAttributes(\n\t\t\t\ttrace.StringAttribute(ochttp.MethodAttribute, httpReq.Method),\n\t\t\t\ttrace.StringAttribute(ochttp.PathAttribute, httpReq.URL.Path),\n\t\t\t)\n\n\t\t\treturn ctx\n\t\t},\n\t)\n\n\tserverFinalizer := jsonrpc.ServerFinalizer(\n\t\tfunc(ctx context.Context, code int, r *http.Request) {\n\t\t\tif span := trace.FromContext(ctx); span != nil {\n\t\t\t\tspan.SetStatus(ochttp.TraceStatus(code, http.StatusText(code)))\n\n\t\t\t\tif rs, ok := ctx.Value(kithttp.ContextKeyResponseSize).(int64); ok {\n\t\t\t\t\tspan.AddAttributes(\n\t\t\t\t\t\ttrace.Int64Attribute(\"http.response_size\", rs),\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\tspan.End()\n\t\t\t}\n\t\t},\n\t)\n\n\treturn func(s *jsonrpc.Server) {\n\t\tserverBeforeCodec(s)\n\t\tserverFinalizer(s)\n\t}\n}\n"
  },
  {
    "path": "tracing/opencensus/jsonrpc_test.go",
    "content": "package opencensus_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"go.opencensus.io/plugin/ochttp\"\n\t\"go.opencensus.io/plugin/ochttp/propagation/b3\"\n\t\"go.opencensus.io/plugin/ochttp/propagation/tracecontext\"\n\t\"go.opencensus.io/trace\"\n\t\"go.opencensus.io/trace/propagation\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\tockit \"github.com/go-kit/kit/tracing/opencensus\"\n\tjsonrpc \"github.com/go-kit/kit/transport/http/jsonrpc\"\n)\n\nfunc TestJSONRPCClientTrace(t *testing.T) {\n\tt.Skip(\"FLAKY\")\n\n\tvar (\n\t\terr          error\n\t\trec          = &recordingExporter{}\n\t\trURL, _      = url.Parse(\"https://httpbin.org/anything\")\n\t\tendpointName = \"DummyEndpoint\"\n\t)\n\n\ttrace.RegisterExporter(rec)\n\n\ttraces := []struct {\n\t\tname string\n\t\terr  error\n\t}{\n\t\t{\"\", nil},\n\t\t{\"CustomName\", nil},\n\t\t{\"\", errors.New(\"dummy-error\")},\n\t}\n\n\tfor _, tr := range traces {\n\t\tclientTracer := ockit.JSONRPCClientTrace(\n\t\t\tockit.WithName(tr.name),\n\t\t\tockit.WithSampler(trace.AlwaysSample()),\n\t\t)\n\t\tep := jsonrpc.NewClient(\n\t\t\trURL,\n\t\t\tendpointName,\n\t\t\tjsonrpc.ClientRequestEncoder(func(ctx context.Context, i interface{}) (json.RawMessage, error) {\n\t\t\t\treturn json.RawMessage(`{}`), nil\n\t\t\t}),\n\t\t\tjsonrpc.ClientResponseDecoder(func(ctx context.Context, r jsonrpc.Response) (response interface{}, err error) {\n\t\t\t\treturn nil, tr.err\n\t\t\t}),\n\t\t\tclientTracer,\n\t\t).Endpoint()\n\n\t\tctx, parentSpan := trace.StartSpan(context.Background(), \"test\")\n\n\t\t_, err = ep(ctx, nil)\n\t\tif want, have := tr.err, err; want != have {\n\t\t\tt.Fatalf(\"unexpected error, want %v, have %v\", tr.err, err)\n\t\t}\n\n\t\tspans := rec.Flush()\n\t\tif want, have := 1, len(spans); want != have {\n\t\t\tt.Fatalf(\"incorrect number of spans, want %d, have %d\", want, have)\n\t\t}\n\n\t\tspan := spans[0]\n\t\tif want, have := parentSpan.SpanContext().SpanID, span.ParentSpanID; want != have {\n\t\t\tt.Errorf(\"incorrect parent ID, want %s, have %s\", want, have)\n\t\t}\n\n\t\tif want, have := tr.name, span.Name; want != have && want != \"\" {\n\t\t\tt.Errorf(\"incorrect span name, want %s, have %s\", want, have)\n\t\t}\n\n\t\tif want, have := endpointName, span.Name; want != have && tr.name == \"\" {\n\t\t\tt.Errorf(\"incorrect span name, want %s, have %s\", want, have)\n\t\t}\n\n\t\tcode := trace.StatusCodeOK\n\t\tif tr.err != nil {\n\t\t\tcode = trace.StatusCodeUnknown\n\n\t\t\tif want, have := err.Error(), span.Status.Message; want != have {\n\t\t\t\tt.Errorf(\"incorrect span status msg, want %s, have %s\", want, have)\n\t\t\t}\n\t\t}\n\n\t\tif want, have := int32(code), span.Status.Code; want != have {\n\t\t\tt.Errorf(\"incorrect span status code, want %d, have %d\", want, have)\n\t\t}\n\t}\n}\n\nfunc TestJSONRPCServerTrace(t *testing.T) {\n\tvar (\n\t\tendpointName = \"DummyEndpoint\"\n\t\trec          = &recordingExporter{}\n\t)\n\n\ttrace.RegisterExporter(rec)\n\n\ttraces := []struct {\n\t\tuseParent   bool\n\t\tname        string\n\t\terr         error\n\t\tpropagation propagation.HTTPFormat\n\t}{\n\t\t{false, \"\", nil, nil},\n\t\t{true, \"\", nil, nil},\n\t\t{true, \"CustomName\", nil, &b3.HTTPFormat{}},\n\t\t{true, \"\", errors.New(\"dummy-error\"), &tracecontext.HTTPFormat{}},\n\t}\n\n\tfor _, tr := range traces {\n\t\tvar client http.Client\n\n\t\thandler := jsonrpc.NewServer(\n\t\t\tjsonrpc.EndpointCodecMap{\n\t\t\t\tendpointName: jsonrpc.EndpointCodec{\n\t\t\t\t\tEndpoint: endpoint.Nop,\n\t\t\t\t\tDecode:   func(context.Context, json.RawMessage) (interface{}, error) { return nil, nil },\n\t\t\t\t\tEncode:   func(context.Context, interface{}) (json.RawMessage, error) { return nil, tr.err },\n\t\t\t\t},\n\t\t\t},\n\t\t\tockit.JSONRPCServerTrace(\n\t\t\t\tockit.WithName(tr.name),\n\t\t\t\tockit.WithSampler(trace.AlwaysSample()),\n\t\t\t\tockit.WithHTTPPropagation(tr.propagation),\n\t\t\t),\n\t\t)\n\n\t\tserver := httptest.NewServer(handler)\n\t\tdefer server.Close()\n\n\t\tjsonStr := []byte(fmt.Sprintf(`{\"method\":\"%s\"}`, endpointName))\n\t\treq, err := http.NewRequest(\"POST\", server.URL, bytes.NewBuffer(jsonStr))\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"unable to create JSONRPC request: %v\", err)\n\t\t}\n\n\t\tif tr.useParent {\n\t\t\tclient = http.Client{\n\t\t\t\tTransport: &ochttp.Transport{\n\t\t\t\t\tStartOptions: trace.StartOptions{\n\t\t\t\t\t\tSampler: trace.AlwaysSample(),\n\t\t\t\t\t},\n\t\t\t\t\tPropagation: tr.propagation,\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\n\t\tresp, err := client.Do(req.WithContext(context.Background()))\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"unable to send JSONRPC request: %v\", err)\n\t\t}\n\t\tresp.Body.Close()\n\n\t\tspans := rec.Flush()\n\n\t\texpectedSpans := 1\n\t\tif tr.useParent {\n\t\t\texpectedSpans++\n\t\t}\n\n\t\tif want, have := expectedSpans, len(spans); want != have {\n\t\t\tt.Fatalf(\"incorrect number of spans, want %d, have %d\", want, have)\n\t\t}\n\n\t\tif tr.useParent {\n\t\t\tif want, have := spans[1].TraceID, spans[0].TraceID; want != have {\n\t\t\t\tt.Errorf(\"incorrect trace ID, want %s, have %s\", want, have)\n\t\t\t}\n\n\t\t\tif want, have := spans[1].SpanID, spans[0].ParentSpanID; want != have {\n\t\t\t\tt.Errorf(\"incorrect span ID, want %s, have %s\", want, have)\n\t\t\t}\n\t\t}\n\n\t\tif want, have := tr.name, spans[0].Name; want != have && want != \"\" {\n\t\t\tt.Errorf(\"incorrect span name, want %s, have %s\", want, have)\n\t\t}\n\n\t\tif want, have := endpointName, spans[0].Name; want != have && tr.name == \"\" {\n\t\t\tt.Errorf(\"incorrect span name, want %s, have %s\", want, have)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tracing/opencensus/opencensus_test.go",
    "content": "package opencensus_test\n\nimport (\n\t\"sync\"\n\n\t\"go.opencensus.io/trace\"\n)\n\ntype recordingExporter struct {\n\tmu   sync.Mutex\n\tdata []*trace.SpanData\n}\n\nfunc (e *recordingExporter) ExportSpan(d *trace.SpanData) {\n\te.mu.Lock()\n\tdefer e.mu.Unlock()\n\n\te.data = append(e.data, d)\n}\n\nfunc (e *recordingExporter) Flush() (data []*trace.SpanData) {\n\te.mu.Lock()\n\tdefer e.mu.Unlock()\n\n\tdata = e.data\n\te.data = nil\n\treturn\n}\n"
  },
  {
    "path": "tracing/opencensus/tracer_options.go",
    "content": "package opencensus\n\nimport (\n\t\"go.opencensus.io/plugin/ochttp/propagation/b3\"\n\t\"go.opencensus.io/trace\"\n\t\"go.opencensus.io/trace/propagation\"\n)\n\n// defaultHTTPPropagate holds OpenCensus' default HTTP propagation format which\n// currently is Zipkin's B3.\nvar defaultHTTPPropagate propagation.HTTPFormat = &b3.HTTPFormat{}\n\n// TracerOption allows for functional options to our OpenCensus tracing\n// middleware.\ntype TracerOption func(o *TracerOptions)\n\n// WithTracerConfig sets all configuration options at once.\nfunc WithTracerConfig(options TracerOptions) TracerOption {\n\treturn func(o *TracerOptions) {\n\t\t*o = options\n\t}\n}\n\n// WithSampler sets the sampler to use by our OpenCensus Tracer.\nfunc WithSampler(sampler trace.Sampler) TracerOption {\n\treturn func(o *TracerOptions) {\n\t\to.Sampler = sampler\n\t}\n}\n\n// WithName sets the name for an instrumented transport endpoint. If name is omitted\n// at tracing middleware creation, the method of the transport or transport rpc\n// name is used.\nfunc WithName(name string) TracerOption {\n\treturn func(o *TracerOptions) {\n\t\to.Name = name\n\t}\n}\n\n// IsPublic should be set to true for publicly accessible servers and for\n// clients that should not propagate their current trace metadata.\n// On the server side a new trace will always be started regardless of any\n// trace metadata being found in the incoming request. If any trace metadata\n// is found, it will be added as a linked trace instead.\nfunc IsPublic(isPublic bool) TracerOption {\n\treturn func(o *TracerOptions) {\n\t\to.Public = isPublic\n\t}\n}\n\n// WithHTTPPropagation sets the propagation handlers for the HTTP transport\n// middlewares. If used on a non HTTP transport this is a noop.\nfunc WithHTTPPropagation(p propagation.HTTPFormat) TracerOption {\n\treturn func(o *TracerOptions) {\n\t\tif p == nil {\n\t\t\t// reset to default OC HTTP format\n\t\t\to.HTTPPropagate = defaultHTTPPropagate\n\t\t\treturn\n\t\t}\n\t\to.HTTPPropagate = p\n\t}\n}\n\n// TracerOptions holds configuration for our tracing middlewares\ntype TracerOptions struct {\n\tSampler       trace.Sampler\n\tName          string\n\tPublic        bool\n\tHTTPPropagate propagation.HTTPFormat\n}\n"
  },
  {
    "path": "tracing/opentracing/doc.go",
    "content": "// Package opentracing provides Go kit integration to the OpenTracing project.\n// OpenTracing implements a general purpose interface that microservices can\n// program against, and which adapts to all major distributed tracing systems.\npackage opentracing\n"
  },
  {
    "path": "tracing/opentracing/endpoint.go",
    "content": "package opentracing\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\n\t\"github.com/opentracing/opentracing-go\"\n\totext \"github.com/opentracing/opentracing-go/ext\"\n\totlog \"github.com/opentracing/opentracing-go/log\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/sd/lb\"\n)\n\n// TraceEndpoint returns a Middleware that wraps the `next` Endpoint in an\n// OpenTracing Span called `operationName`.\n//\n// If `ctx` already has a Span, child span is created from it.\n// If `ctx` doesn't yet have a Span, the new one is created.\nfunc TraceEndpoint(tracer opentracing.Tracer, operationName string, opts ...EndpointOption) endpoint.Middleware {\n\tcfg := &EndpointOptions{\n\t\tTags: make(opentracing.Tags),\n\t}\n\n\tfor _, opt := range opts {\n\t\topt(cfg)\n\t}\n\n\treturn func(next endpoint.Endpoint) endpoint.Endpoint {\n\t\treturn func(ctx context.Context, request interface{}) (response interface{}, err error) {\n\t\t\tif cfg.GetOperationName != nil {\n\t\t\t\tif newOperationName := cfg.GetOperationName(ctx, operationName); newOperationName != \"\" {\n\t\t\t\t\toperationName = newOperationName\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvar span opentracing.Span\n\t\t\tif parentSpan := opentracing.SpanFromContext(ctx); parentSpan != nil {\n\t\t\t\tspan = tracer.StartSpan(\n\t\t\t\t\toperationName,\n\t\t\t\t\topentracing.ChildOf(parentSpan.Context()),\n\t\t\t\t)\n\t\t\t} else {\n\t\t\t\tspan = tracer.StartSpan(operationName)\n\t\t\t}\n\t\t\tdefer span.Finish()\n\n\t\t\tapplyTags(span, cfg.Tags)\n\t\t\tif cfg.GetTags != nil {\n\t\t\t\textraTags := cfg.GetTags(ctx)\n\t\t\t\tapplyTags(span, extraTags)\n\t\t\t}\n\n\t\t\tctx = opentracing.ContextWithSpan(ctx, span)\n\n\t\t\tdefer func() {\n\t\t\t\tif err != nil {\n\t\t\t\t\tif lbErr, ok := err.(lb.RetryError); ok {\n\t\t\t\t\t\t// handle errors originating from lb.Retry\n\t\t\t\t\t\tfields := make([]otlog.Field, 0, len(lbErr.RawErrors))\n\t\t\t\t\t\tfor idx, rawErr := range lbErr.RawErrors {\n\t\t\t\t\t\t\tfields = append(fields, otlog.String(\n\t\t\t\t\t\t\t\t\"gokit.retry.error.\"+strconv.Itoa(idx+1), rawErr.Error(),\n\t\t\t\t\t\t\t))\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\totext.LogError(span, lbErr, fields...)\n\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\t// generic error\n\t\t\t\t\totext.LogError(span, err)\n\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// test for business error\n\t\t\t\tif res, ok := response.(endpoint.Failer); ok && res.Failed() != nil {\n\t\t\t\t\tspan.LogFields(\n\t\t\t\t\t\totlog.String(\"gokit.business.error\", res.Failed().Error()),\n\t\t\t\t\t)\n\n\t\t\t\t\tif cfg.IgnoreBusinessError {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\t// treating business error as real error in span.\n\t\t\t\t\totext.LogError(span, res.Failed())\n\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\treturn next(ctx, request)\n\t\t}\n\t}\n}\n\n// TraceServer returns a Middleware that wraps the `next` Endpoint in an\n// OpenTracing Span called `operationName` with server span.kind tag..\nfunc TraceServer(tracer opentracing.Tracer, operationName string, opts ...EndpointOption) endpoint.Middleware {\n\topts = append(opts, WithTags(map[string]interface{}{\n\t\totext.SpanKindRPCServer.Key: otext.SpanKindRPCServer.Value,\n\t}))\n\n\treturn TraceEndpoint(tracer, operationName, opts...)\n}\n\n// TraceClient returns a Middleware that wraps the `next` Endpoint in an\n// OpenTracing Span called `operationName` with client span.kind tag.\nfunc TraceClient(tracer opentracing.Tracer, operationName string, opts ...EndpointOption) endpoint.Middleware {\n\topts = append(opts, WithTags(map[string]interface{}{\n\t\totext.SpanKindRPCClient.Key: otext.SpanKindRPCClient.Value,\n\t}))\n\n\treturn TraceEndpoint(tracer, operationName, opts...)\n}\n\nfunc applyTags(span opentracing.Span, tags opentracing.Tags) {\n\tfor key, value := range tags {\n\t\tspan.SetTag(key, value)\n\t}\n}\n"
  },
  {
    "path": "tracing/opentracing/endpoint_options.go",
    "content": "package opentracing\n\nimport (\n\t\"context\"\n\n\t\"github.com/opentracing/opentracing-go\"\n)\n\n// EndpointOptions holds the options for tracing an endpoint\ntype EndpointOptions struct {\n\t// IgnoreBusinessError if set to true will not treat a business error\n\t// identified through the endpoint.Failer interface as a span error.\n\tIgnoreBusinessError bool\n\n\t// GetOperationName is an optional function that can set the span operation name based on the existing one\n\t// for the endpoint and information in the context.\n\t//\n\t// If the function is nil, or the returned name is empty, the existing name for the endpoint is used.\n\tGetOperationName func(ctx context.Context, name string) string\n\n\t// Tags holds the default tags which will be set on span\n\t// creation by our Endpoint middleware.\n\tTags opentracing.Tags\n\n\t// GetTags is an optional function that can extract tags\n\t// from the context and add them to the span.\n\tGetTags func(ctx context.Context) opentracing.Tags\n}\n\n// EndpointOption allows for functional options to endpoint tracing middleware.\ntype EndpointOption func(*EndpointOptions)\n\n// WithOptions sets all configuration options at once by use of the EndpointOptions struct.\nfunc WithOptions(options EndpointOptions) EndpointOption {\n\treturn func(o *EndpointOptions) {\n\t\t*o = options\n\t}\n}\n\n// WithIgnoreBusinessError if set to true will not treat a business error\n// identified through the endpoint.Failer interface as a span error.\nfunc WithIgnoreBusinessError(ignoreBusinessError bool) EndpointOption {\n\treturn func(o *EndpointOptions) {\n\t\to.IgnoreBusinessError = ignoreBusinessError\n\t}\n}\n\n// WithOperationNameFunc allows to set function that can set the span operation name based on the existing one\n// for the endpoint and information in the context.\nfunc WithOperationNameFunc(getOperationName func(ctx context.Context, name string) string) EndpointOption {\n\treturn func(o *EndpointOptions) {\n\t\to.GetOperationName = getOperationName\n\t}\n}\n\n// WithTags adds default tags for the spans created by the Endpoint tracer.\nfunc WithTags(tags opentracing.Tags) EndpointOption {\n\treturn func(o *EndpointOptions) {\n\t\tif o.Tags == nil {\n\t\t\to.Tags = make(opentracing.Tags)\n\t\t}\n\n\t\tfor key, value := range tags {\n\t\t\to.Tags[key] = value\n\t\t}\n\t}\n}\n\n// WithTagsFunc set the func to extracts additional tags from the context.\nfunc WithTagsFunc(getTags func(ctx context.Context) opentracing.Tags) EndpointOption {\n\treturn func(o *EndpointOptions) {\n\t\to.GetTags = getTags\n\t}\n}\n"
  },
  {
    "path": "tracing/opentracing/endpoint_test.go",
    "content": "package opentracing_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/opentracing/opentracing-go\"\n\totext \"github.com/opentracing/opentracing-go/ext\"\n\totlog \"github.com/opentracing/opentracing-go/log\"\n\t\"github.com/opentracing/opentracing-go/mocktracer\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/sd\"\n\t\"github.com/go-kit/kit/sd/lb\"\n\tkitot \"github.com/go-kit/kit/tracing/opentracing\"\n)\n\nconst (\n\tspan1 = \"SPAN-1\"\n\tspan2 = \"SPAN-2\"\n\tspan3 = \"SPAN-3\"\n\tspan4 = \"SPAN-4\"\n\tspan5 = \"SPAN-5\"\n\tspan6 = \"SPAN-6\"\n\tspan7 = \"SPAN-7\"\n\tspan8 = \"SPAN-8\"\n)\n\nvar (\n\terr1 = errors.New(\"some error\")\n\terr2 = errors.New(\"some business error\")\n\terr3 = errors.New(\"other business error\")\n)\n\n// compile time assertion\nvar _ endpoint.Failer = failedResponse{}\n\ntype failedResponse struct {\n\terr error\n}\n\nfunc (r failedResponse) Failed() error {\n\treturn r.err\n}\n\nfunc TestTraceEndpoint(t *testing.T) {\n\ttracer := mocktracer.New()\n\n\t// Initialize the ctx with a parent Span.\n\tparentSpan := tracer.StartSpan(\"parent\").(*mocktracer.MockSpan)\n\tdefer parentSpan.Finish()\n\tctx := opentracing.ContextWithSpan(context.Background(), parentSpan)\n\n\ttracedEndpoint := kitot.TraceEndpoint(tracer, \"testOp\")(endpoint.Nop)\n\tif _, err := tracedEndpoint(ctx, struct{}{}); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// tracedEndpoint created a new Span.\n\tfinishedSpans := tracer.FinishedSpans()\n\tif want, have := 1, len(finishedSpans); want != have {\n\t\tt.Fatalf(\"Want %v span(s), found %v\", want, have)\n\t}\n\n\tendpointSpan := finishedSpans[0]\n\tif want, have := \"testOp\", endpointSpan.OperationName; want != have {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n\n\tparentContext := parentSpan.Context().(mocktracer.MockSpanContext)\n\tendpointContext := parentSpan.Context().(mocktracer.MockSpanContext)\n\n\t// ... and that the parent ID is set appropriately.\n\tif want, have := parentContext.SpanID, endpointContext.SpanID; want != have {\n\t\tt.Errorf(\"Want ParentID %q, have %q\", want, have)\n\t}\n}\n\nfunc TestTraceEndpointNoContextSpan(t *testing.T) {\n\ttracer := mocktracer.New()\n\n\t// Empty/background context.\n\ttracedEndpoint := kitot.TraceEndpoint(tracer, \"testOp\")(endpoint.Nop)\n\tif _, err := tracedEndpoint(context.Background(), struct{}{}); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// tracedEndpoint created a new Span.\n\tfinishedSpans := tracer.FinishedSpans()\n\tif want, have := 1, len(finishedSpans); want != have {\n\t\tt.Fatalf(\"Want %v span(s), found %v\", want, have)\n\t}\n\n\tendpointSpan := finishedSpans[0]\n\n\tif want, have := \"testOp\", endpointSpan.OperationName; want != have {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n}\n\nfunc TestTraceEndpointWithOptions(t *testing.T) {\n\ttracer := mocktracer.New()\n\n\t// span 1 without options\n\tmw := kitot.TraceEndpoint(tracer, span1)\n\ttracedEndpoint := mw(endpoint.Nop)\n\t_, _ = tracedEndpoint(context.Background(), struct{}{})\n\n\t// span 2 with options\n\tmw = kitot.TraceEndpoint(\n\t\ttracer,\n\t\tspan2,\n\t\tkitot.WithOptions(kitot.EndpointOptions{}),\n\t)\n\ttracedEndpoint = mw(func(context.Context, interface{}) (interface{}, error) {\n\t\treturn nil, err1\n\t})\n\t_, _ = tracedEndpoint(context.Background(), struct{}{})\n\n\t// span 3 with lb error\n\tmw = kitot.TraceEndpoint(\n\t\ttracer,\n\t\tspan3,\n\t\tkitot.WithOptions(kitot.EndpointOptions{}),\n\t)\n\ttracedEndpoint = mw(\n\t\tlb.Retry(\n\t\t\t5,\n\t\t\t1*time.Second,\n\t\t\tlb.NewRoundRobin(\n\t\t\t\tsd.FixedEndpointer{\n\t\t\t\t\tfunc(context.Context, interface{}) (interface{}, error) {\n\t\t\t\t\t\treturn nil, err1\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t),\n\t\t),\n\t)\n\t_, _ = tracedEndpoint(context.Background(), struct{}{})\n\n\t// span 4 with disabled IgnoreBusinessError option\n\tmw = kitot.TraceEndpoint(\n\t\ttracer,\n\t\tspan4,\n\t\tkitot.WithIgnoreBusinessError(false),\n\t)\n\ttracedEndpoint = mw(func(context.Context, interface{}) (interface{}, error) {\n\t\treturn failedResponse{\n\t\t\terr: err2,\n\t\t}, nil\n\t})\n\t_, _ = tracedEndpoint(context.Background(), struct{}{})\n\n\t// span 5 with enabled IgnoreBusinessError option\n\tmw = kitot.TraceEndpoint(tracer, span5, kitot.WithIgnoreBusinessError(true))\n\ttracedEndpoint = mw(func(context.Context, interface{}) (interface{}, error) {\n\t\treturn failedResponse{\n\t\t\terr: err3,\n\t\t}, nil\n\t})\n\t_, _ = tracedEndpoint(context.Background(), struct{}{})\n\n\t// span 6 with OperationNameFunc option\n\tmw = kitot.TraceEndpoint(\n\t\ttracer,\n\t\tspan6,\n\t\tkitot.WithOperationNameFunc(func(ctx context.Context, name string) string {\n\t\t\treturn fmt.Sprintf(\"%s-%s\", \"new\", name)\n\t\t}),\n\t)\n\ttracedEndpoint = mw(endpoint.Nop)\n\t_, _ = tracedEndpoint(context.Background(), struct{}{})\n\n\t// span 7 with Tags options\n\tmw = kitot.TraceEndpoint(\n\t\ttracer,\n\t\tspan7,\n\t\tkitot.WithTags(map[string]interface{}{\n\t\t\t\"tag1\": \"tag1\",\n\t\t\t\"tag2\": \"tag2\",\n\t\t}),\n\t\tkitot.WithTags(map[string]interface{}{\n\t\t\t\"tag3\": \"tag3\",\n\t\t}),\n\t)\n\ttracedEndpoint = mw(endpoint.Nop)\n\t_, _ = tracedEndpoint(context.Background(), struct{}{})\n\n\t// span 8 with TagsFunc options\n\tmw = kitot.TraceEndpoint(\n\t\ttracer,\n\t\tspan8,\n\t\tkitot.WithTags(map[string]interface{}{\n\t\t\t\"tag1\": \"tag1\",\n\t\t\t\"tag2\": \"tag2\",\n\t\t}),\n\t\tkitot.WithTags(map[string]interface{}{\n\t\t\t\"tag3\": \"tag3\",\n\t\t}),\n\t\tkitot.WithTagsFunc(func(ctx context.Context) opentracing.Tags {\n\t\t\treturn map[string]interface{}{\n\t\t\t\t\"tag4\": \"tag4\",\n\t\t\t}\n\t\t}),\n\t)\n\ttracedEndpoint = mw(endpoint.Nop)\n\t_, _ = tracedEndpoint(context.Background(), struct{}{})\n\n\tfinishedSpans := tracer.FinishedSpans()\n\tif want, have := 8, len(finishedSpans); want != have {\n\t\tt.Fatalf(\"Want %v span(s), found %v\", want, have)\n\t}\n\n\t// test span 1\n\tspan := finishedSpans[0]\n\n\tif want, have := span1, span.OperationName; want != have {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n\n\t// test span 2\n\tspan = finishedSpans[1]\n\n\tif want, have := span2, span.OperationName; want != have {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n\n\tif want, have := true, span.Tag(\"error\"); want != have {\n\t\tt.Fatalf(\"Want %v, have %v\", want, have)\n\t}\n\n\t// test span 3\n\tspan = finishedSpans[2]\n\n\tif want, have := span3, span.OperationName; want != have {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n\n\tif want, have := true, span.Tag(\"error\"); want != have {\n\t\tt.Fatalf(\"Want %v, have %v\", want, have)\n\t}\n\n\tif want, have := 1, len(span.Logs()); want != have {\n\t\tt.Fatalf(\"incorrect logs count, wanted %d, got %d\", want, have)\n\t}\n\n\tif want, have := []otlog.Field{\n\t\totlog.String(\"event\", \"error\"),\n\t\totlog.String(\"error.object\", \"some error (previously: some error; some error; some error; some error)\"),\n\t\totlog.String(\"gokit.retry.error.1\", \"some error\"),\n\t\totlog.String(\"gokit.retry.error.2\", \"some error\"),\n\t\totlog.String(\"gokit.retry.error.3\", \"some error\"),\n\t\totlog.String(\"gokit.retry.error.4\", \"some error\"),\n\t\totlog.String(\"gokit.retry.error.5\", \"some error\"),\n\t}, span.Logs()[0].Fields; reflect.DeepEqual(want, have) {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n\n\t// test span 4\n\tspan = finishedSpans[3]\n\n\tif want, have := span4, span.OperationName; want != have {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n\n\tif want, have := true, span.Tag(\"error\"); want != have {\n\t\tt.Fatalf(\"Want %v, have %v\", want, have)\n\t}\n\n\tif want, have := 2, len(span.Logs()); want != have {\n\t\tt.Fatalf(\"incorrect logs count, wanted %d, got %d\", want, have)\n\t}\n\n\tif want, have := []otlog.Field{\n\t\totlog.String(\"gokit.business.error\", \"some business error\"),\n\t}, span.Logs()[0].Fields; reflect.DeepEqual(want, have) {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n\n\tif want, have := []otlog.Field{\n\t\totlog.String(\"event\", \"error\"),\n\t\totlog.String(\"error.object\", \"some business error\"),\n\t}, span.Logs()[1].Fields; reflect.DeepEqual(want, have) {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n\n\t// test span 5\n\tspan = finishedSpans[4]\n\n\tif want, have := span5, span.OperationName; want != have {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n\n\tif want, have := (interface{})(nil), span.Tag(\"error\"); want != have {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n\n\tif want, have := 1, len(span.Logs()); want != have {\n\t\tt.Fatalf(\"incorrect logs count, wanted %d, got %d\", want, have)\n\t}\n\n\tif want, have := []otlog.Field{\n\t\totlog.String(\"gokit.business.error\", \"some business error\"),\n\t}, span.Logs()[0].Fields; reflect.DeepEqual(want, have) {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n\n\t// test span 6\n\tspan = finishedSpans[5]\n\n\tif want, have := fmt.Sprintf(\"%s-%s\", \"new\", span6), span.OperationName; want != have {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n\n\t// test span 7\n\tspan = finishedSpans[6]\n\n\tif want, have := span7, span.OperationName; want != have {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n\n\tif want, have := map[string]interface{}{\n\t\t\"tag1\": \"tag1\",\n\t\t\"tag2\": \"tag2\",\n\t\t\"tag3\": \"tag3\",\n\t}, span.Tags(); fmt.Sprint(want) != fmt.Sprint(have) {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n\n\t// test span 8\n\tspan = finishedSpans[7]\n\n\tif want, have := span8, span.OperationName; want != have {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n\n\tif want, have := map[string]interface{}{\n\t\t\"tag1\": \"tag1\",\n\t\t\"tag2\": \"tag2\",\n\t\t\"tag3\": \"tag3\",\n\t\t\"tag4\": \"tag4\",\n\t}, span.Tags(); fmt.Sprint(want) != fmt.Sprint(have) {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n}\n\nfunc TestTraceServer(t *testing.T) {\n\ttracer := mocktracer.New()\n\n\t// Empty/background context.\n\ttracedEndpoint := kitot.TraceServer(tracer, \"testOp\")(endpoint.Nop)\n\tif _, err := tracedEndpoint(context.Background(), struct{}{}); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// tracedEndpoint created a new Span.\n\tfinishedSpans := tracer.FinishedSpans()\n\tif want, have := 1, len(finishedSpans); want != have {\n\t\tt.Fatalf(\"Want %v span(s), found %v\", want, have)\n\t}\n\n\tspan := finishedSpans[0]\n\n\tif want, have := \"testOp\", span.OperationName; want != have {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n\n\tif want, have := map[string]interface{}{\n\t\totext.SpanKindRPCServer.Key: otext.SpanKindRPCServer.Value,\n\t}, span.Tags(); fmt.Sprint(want) != fmt.Sprint(have) {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n}\n\nfunc TestTraceClient(t *testing.T) {\n\ttracer := mocktracer.New()\n\n\t// Empty/background context.\n\ttracedEndpoint := kitot.TraceClient(tracer, \"testOp\")(endpoint.Nop)\n\tif _, err := tracedEndpoint(context.Background(), struct{}{}); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// tracedEndpoint created a new Span.\n\tfinishedSpans := tracer.FinishedSpans()\n\tif want, have := 1, len(finishedSpans); want != have {\n\t\tt.Fatalf(\"Want %v span(s), found %v\", want, have)\n\t}\n\n\tspan := finishedSpans[0]\n\n\tif want, have := \"testOp\", span.OperationName; want != have {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n\n\tif want, have := map[string]interface{}{\n\t\totext.SpanKindRPCClient.Key: otext.SpanKindRPCClient.Value,\n\t}, span.Tags(); fmt.Sprint(want) != fmt.Sprint(have) {\n\t\tt.Fatalf(\"Want %q, have %q\", want, have)\n\t}\n}\n"
  },
  {
    "path": "tracing/opentracing/grpc.go",
    "content": "package opentracing\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"strings\"\n\n\t\"github.com/opentracing/opentracing-go\"\n\t\"github.com/opentracing/opentracing-go/ext\"\n\t\"google.golang.org/grpc/metadata\"\n\n\t\"github.com/go-kit/log\"\n)\n\n// ContextToGRPC returns a grpc RequestFunc that injects an OpenTracing Span\n// found in `ctx` into the grpc Metadata. If no such Span can be found, the\n// RequestFunc is a noop.\nfunc ContextToGRPC(tracer opentracing.Tracer, logger log.Logger) func(ctx context.Context, md *metadata.MD) context.Context {\n\treturn func(ctx context.Context, md *metadata.MD) context.Context {\n\t\tif span := opentracing.SpanFromContext(ctx); span != nil {\n\t\t\t// There's nothing we can do with an error here.\n\t\t\tif err := tracer.Inject(span.Context(), opentracing.TextMap, metadataReaderWriter{md}); err != nil {\n\t\t\t\tlogger.Log(\"err\", err)\n\t\t\t}\n\t\t}\n\t\treturn ctx\n\t}\n}\n\n// GRPCToContext returns a grpc RequestFunc that tries to join with an\n// OpenTracing trace found in `req` and starts a new Span called\n// `operationName` accordingly. If no trace could be found in `req`, the Span\n// will be a trace root. The Span is incorporated in the returned Context and\n// can be retrieved with opentracing.SpanFromContext(ctx).\nfunc GRPCToContext(tracer opentracing.Tracer, operationName string, logger log.Logger) func(ctx context.Context, md metadata.MD) context.Context {\n\treturn func(ctx context.Context, md metadata.MD) context.Context {\n\t\tvar span opentracing.Span\n\t\twireContext, err := tracer.Extract(opentracing.TextMap, metadataReaderWriter{&md})\n\t\tif err != nil && err != opentracing.ErrSpanContextNotFound {\n\t\t\tlogger.Log(\"err\", err)\n\t\t}\n\t\tspan = tracer.StartSpan(operationName, ext.RPCServerOption(wireContext))\n\t\treturn opentracing.ContextWithSpan(ctx, span)\n\t}\n}\n\n// A type that conforms to opentracing.TextMapReader and\n// opentracing.TextMapWriter.\ntype metadataReaderWriter struct {\n\t*metadata.MD\n}\n\nfunc (w metadataReaderWriter) Set(key, val string) {\n\tkey = strings.ToLower(key)\n\tif strings.HasSuffix(key, \"-bin\") {\n\t\tval = base64.StdEncoding.EncodeToString([]byte(val))\n\t}\n\t(*w.MD)[key] = append((*w.MD)[key], val)\n}\n\nfunc (w metadataReaderWriter) ForeachKey(handler func(key, val string) error) error {\n\tfor k, vals := range *w.MD {\n\t\tfor _, v := range vals {\n\t\t\tif err := handler(k, v); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "tracing/opentracing/grpc_test.go",
    "content": "package opentracing_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/opentracing/opentracing-go\"\n\t\"github.com/opentracing/opentracing-go/mocktracer\"\n\t\"google.golang.org/grpc/metadata\"\n\n\tkitot \"github.com/go-kit/kit/tracing/opentracing\"\n\t\"github.com/go-kit/log\"\n)\n\nfunc TestTraceGRPCRequestRoundtrip(t *testing.T) {\n\tlogger := log.NewNopLogger()\n\ttracer := mocktracer.New()\n\n\t// Initialize the ctx with a Span to inject.\n\tbeforeSpan := tracer.StartSpan(\"to_inject\").(*mocktracer.MockSpan)\n\tdefer beforeSpan.Finish()\n\tbeforeSpan.SetBaggageItem(\"baggage\", \"check\")\n\tbeforeCtx := opentracing.ContextWithSpan(context.Background(), beforeSpan)\n\n\ttoGRPCFunc := kitot.ContextToGRPC(tracer, logger)\n\tmd := metadata.Pairs()\n\t// Call the RequestFunc.\n\tafterCtx := toGRPCFunc(beforeCtx, &md)\n\n\t// The Span should not have changed.\n\tafterSpan := opentracing.SpanFromContext(afterCtx)\n\tif beforeSpan != afterSpan {\n\t\tt.Error(\"Should not swap in a new span\")\n\t}\n\n\t// No spans should have finished yet.\n\tfinishedSpans := tracer.FinishedSpans()\n\tif want, have := 0, len(finishedSpans); want != have {\n\t\tt.Errorf(\"Want %v span(s), found %v\", want, have)\n\t}\n\n\t// Use GRPCToContext to verify that we can join with the trace given MD.\n\tfromGRPCFunc := kitot.GRPCToContext(tracer, \"joined\", logger)\n\tjoinCtx := fromGRPCFunc(afterCtx, md)\n\tjoinedSpan := opentracing.SpanFromContext(joinCtx).(*mocktracer.MockSpan)\n\n\tjoinedContext := joinedSpan.Context().(mocktracer.MockSpanContext)\n\tbeforeContext := beforeSpan.Context().(mocktracer.MockSpanContext)\n\n\tif joinedContext.SpanID == beforeContext.SpanID {\n\t\tt.Error(\"SpanID should have changed\", joinedContext.SpanID, beforeContext.SpanID)\n\t}\n\n\t// Check that the parent/child relationship is as expected for the joined span.\n\tif want, have := beforeContext.SpanID, joinedSpan.ParentID; want != have {\n\t\tt.Errorf(\"Want ParentID %q, have %q\", want, have)\n\t}\n\tif want, have := \"joined\", joinedSpan.OperationName; want != have {\n\t\tt.Errorf(\"Want %q, have %q\", want, have)\n\t}\n\tif want, have := \"check\", joinedSpan.BaggageItem(\"baggage\"); want != have {\n\t\tt.Errorf(\"Want %q, have %q\", want, have)\n\t}\n}\n"
  },
  {
    "path": "tracing/opentracing/http.go",
    "content": "package opentracing\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/http\"\n\t\"strconv\"\n\n\topentracing \"github.com/opentracing/opentracing-go\"\n\t\"github.com/opentracing/opentracing-go/ext\"\n\n\tkithttp \"github.com/go-kit/kit/transport/http\"\n\t\"github.com/go-kit/log\"\n)\n\n// ContextToHTTP returns an http RequestFunc that injects an OpenTracing Span\n// found in `ctx` into the http headers. If no such Span can be found, the\n// RequestFunc is a noop.\nfunc ContextToHTTP(tracer opentracing.Tracer, logger log.Logger) kithttp.RequestFunc {\n\treturn func(ctx context.Context, req *http.Request) context.Context {\n\t\t// Try to find a Span in the Context.\n\t\tif span := opentracing.SpanFromContext(ctx); span != nil {\n\t\t\t// Add standard OpenTracing tags.\n\t\t\text.HTTPMethod.Set(span, req.Method)\n\t\t\text.HTTPUrl.Set(span, req.URL.String())\n\t\t\thost, portString, err := net.SplitHostPort(req.URL.Host)\n\t\t\tif err == nil {\n\t\t\t\text.PeerHostname.Set(span, host)\n\t\t\t\tif port, err := strconv.Atoi(portString); err == nil {\n\t\t\t\t\text.PeerPort.Set(span, uint16(port))\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\text.PeerHostname.Set(span, req.URL.Host)\n\t\t\t}\n\n\t\t\t// There's nothing we can do with any errors here.\n\t\t\tif err = tracer.Inject(\n\t\t\t\tspan.Context(),\n\t\t\t\topentracing.HTTPHeaders,\n\t\t\t\topentracing.HTTPHeadersCarrier(req.Header),\n\t\t\t); err != nil {\n\t\t\t\tlogger.Log(\"err\", err)\n\t\t\t}\n\t\t}\n\t\treturn ctx\n\t}\n}\n\n// HTTPToContext returns an http RequestFunc that tries to join with an\n// OpenTracing trace found in `req` and starts a new Span called\n// `operationName` accordingly. If no trace could be found in `req`, the Span\n// will be a trace root. The Span is incorporated in the returned Context and\n// can be retrieved with opentracing.SpanFromContext(ctx).\nfunc HTTPToContext(tracer opentracing.Tracer, operationName string, logger log.Logger) kithttp.RequestFunc {\n\treturn func(ctx context.Context, req *http.Request) context.Context {\n\t\t// Try to join to a trace propagated in `req`.\n\t\tvar span opentracing.Span\n\t\twireContext, err := tracer.Extract(\n\t\t\topentracing.HTTPHeaders,\n\t\t\topentracing.HTTPHeadersCarrier(req.Header),\n\t\t)\n\t\tif err != nil && err != opentracing.ErrSpanContextNotFound {\n\t\t\tlogger.Log(\"err\", err)\n\t\t}\n\n\t\tspan = tracer.StartSpan(operationName, ext.RPCServerOption(wireContext))\n\t\text.HTTPMethod.Set(span, req.Method)\n\t\text.HTTPUrl.Set(span, req.URL.String())\n\t\treturn opentracing.ContextWithSpan(ctx, span)\n\t}\n}\n"
  },
  {
    "path": "tracing/opentracing/http_test.go",
    "content": "package opentracing_test\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"testing\"\n\n\topentracing \"github.com/opentracing/opentracing-go\"\n\t\"github.com/opentracing/opentracing-go/ext\"\n\t\"github.com/opentracing/opentracing-go/mocktracer\"\n\n\tkitot \"github.com/go-kit/kit/tracing/opentracing\"\n\t\"github.com/go-kit/log\"\n)\n\nfunc TestTraceHTTPRequestRoundtrip(t *testing.T) {\n\tlogger := log.NewNopLogger()\n\ttracer := mocktracer.New()\n\n\t// Initialize the ctx with a Span to inject.\n\tbeforeSpan := tracer.StartSpan(\"to_inject\").(*mocktracer.MockSpan)\n\tdefer beforeSpan.Finish()\n\tbeforeSpan.SetBaggageItem(\"baggage\", \"check\")\n\tbeforeCtx := opentracing.ContextWithSpan(context.Background(), beforeSpan)\n\n\ttoHTTPFunc := kitot.ContextToHTTP(tracer, logger)\n\treq, _ := http.NewRequest(\"GET\", \"http://test.biz/path\", nil)\n\t// Call the RequestFunc.\n\tafterCtx := toHTTPFunc(beforeCtx, req)\n\n\t// The Span should not have changed.\n\tafterSpan := opentracing.SpanFromContext(afterCtx)\n\tif beforeSpan != afterSpan {\n\t\tt.Error(\"Should not swap in a new span\")\n\t}\n\n\t// No spans should have finished yet.\n\tfinishedSpans := tracer.FinishedSpans()\n\tif want, have := 0, len(finishedSpans); want != have {\n\t\tt.Errorf(\"Want %v span(s), found %v\", want, have)\n\t}\n\n\t// Use HTTPToContext to verify that we can join with the trace given a req.\n\tfromHTTPFunc := kitot.HTTPToContext(tracer, \"joined\", logger)\n\tjoinCtx := fromHTTPFunc(afterCtx, req)\n\tjoinedSpan := opentracing.SpanFromContext(joinCtx).(*mocktracer.MockSpan)\n\n\tjoinedContext := joinedSpan.Context().(mocktracer.MockSpanContext)\n\tbeforeContext := beforeSpan.Context().(mocktracer.MockSpanContext)\n\n\tif joinedContext.SpanID == beforeContext.SpanID {\n\t\tt.Error(\"SpanID should have changed\", joinedContext.SpanID, beforeContext.SpanID)\n\t}\n\n\t// Check that the parent/child relationship is as expected for the joined span.\n\tif want, have := beforeContext.SpanID, joinedSpan.ParentID; want != have {\n\t\tt.Errorf(\"Want ParentID %q, have %q\", want, have)\n\t}\n\tif want, have := \"joined\", joinedSpan.OperationName; want != have {\n\t\tt.Errorf(\"Want %q, have %q\", want, have)\n\t}\n\tif want, have := \"check\", joinedSpan.BaggageItem(\"baggage\"); want != have {\n\t\tt.Errorf(\"Want %q, have %q\", want, have)\n\t}\n}\n\nfunc TestContextToHTTPTags(t *testing.T) {\n\ttracer := mocktracer.New()\n\tspan := tracer.StartSpan(\"to_inject\").(*mocktracer.MockSpan)\n\tdefer span.Finish()\n\tctx := opentracing.ContextWithSpan(context.Background(), span)\n\treq, _ := http.NewRequest(\"GET\", \"http://test.biz/path\", nil)\n\n\tkitot.ContextToHTTP(tracer, log.NewNopLogger())(ctx, req)\n\n\texpectedTags := map[string]interface{}{\n\t\tstring(ext.HTTPMethod):   \"GET\",\n\t\tstring(ext.HTTPUrl):      \"http://test.biz/path\",\n\t\tstring(ext.PeerHostname): \"test.biz\",\n\t}\n\tif !reflect.DeepEqual(expectedTags, span.Tags()) {\n\t\tt.Errorf(\"Want %q, have %q\", expectedTags, span.Tags())\n\t}\n}\n\nfunc TestHTTPToContextTags(t *testing.T) {\n\ttracer := mocktracer.New()\n\tparentSpan := tracer.StartSpan(\"to_extract\").(*mocktracer.MockSpan)\n\tdefer parentSpan.Finish()\n\treq, _ := http.NewRequest(\"GET\", \"http://test.biz/path\", nil)\n\ttracer.Inject(parentSpan.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))\n\n\tctx := kitot.HTTPToContext(tracer, \"op\", log.NewNopLogger())(context.Background(), req)\n\topentracing.SpanFromContext(ctx).Finish()\n\n\tchildSpan := tracer.FinishedSpans()[0]\n\texpectedTags := map[string]interface{}{\n\t\tstring(ext.HTTPMethod): \"GET\",\n\t\tstring(ext.HTTPUrl):    \"http://test.biz/path\",\n\t\tstring(ext.SpanKind):   ext.SpanKindRPCServerEnum,\n\t}\n\tif !reflect.DeepEqual(expectedTags, childSpan.Tags()) {\n\t\tt.Errorf(\"Want %q, have %q\", expectedTags, childSpan.Tags())\n\t}\n\tif want, have := \"op\", childSpan.OperationName; want != have {\n\t\tt.Errorf(\"Want %q, have %q\", want, have)\n\t}\n}\n"
  },
  {
    "path": "tracing/zipkin/README.md",
    "content": "# Zipkin\n\n## Development and Testing Set-up\n\nGreat efforts have been made to make [Zipkin] easier to test, develop and\nexperiment against. [Zipkin] can now be run from a single Docker container or by\nrunning its self-contained executable jar without extensive configuration. In\nits default configuration you will run [Zipkin] with a HTTP collector, In memory\nSpan storage backend and web UI on port 9411.\n\nExample:\n```\ndocker run -d -p 9411:9411 openzipkin/zipkin\n```\n\n[zipkin]: http://zipkin.io\n\n## Middleware Usage\n\nFollow the [addsvc] example to check out how to wire the [Zipkin] Middleware.\nThe changes should be relatively minor.\n\nThe [zipkin-go] package has Reporters to send Spans to the [Zipkin] HTTP and\nKafka Collectors.\n\n### Configuring the Zipkin HTTP Reporter\n\nTo use the HTTP Reporter with a [Zipkin] instance running on localhost you\nbootstrap [zipkin-go] like this:\n\n```go\nvar (\n  serviceName        = \"MyService\"\n  serviceHostPort    = \"localhost:8000\"\n  zipkinHTTPEndpoint = \"http://localhost:9411/api/v2/spans\"\n)\n\n// create an instance of the HTTP Reporter.\nreporter := zipkin.NewReporter(zipkinHTTPEndpoint)\n\n// create our tracer's local endpoint (how the service is identified in Zipkin).\nlocalEndpoint, err := zipkin.NewEndpoint(serviceName, serviceHostPort)\n\n// create our tracer instance.\ntracer, err = zipkin.NewTracer(reporter, zipkin.WithLocalEndpoint(localEndpoint))\n  ...\n\n```\n\n[zipkin-go]: https://github.com/openzipkin/zipkin-go\n[addsvc]: https://github.com/go-kit/examples/tree/master/addsvc\n[Log]: https://github.com/go-kit/kit/tree/master/log\n\n### Tracing Resources\n\nHere is an example of how you could trace resources and work with local spans.\n```go\nimport (\n\tzipkin \"github.com/openzipkin/zipkin-go\"\n)\n\nfunc (svc *Service) GetMeSomeExamples(ctx context.Context, ...) ([]Examples, error) {\n  // Example of annotating a database query:\n  var (\n    spanContext model.SpanContext\n    serviceName = \"MySQL\"\n    serviceHost = \"mysql.example.com:3306\"\n    queryLabel  = \"GetExamplesByParam\"\n    query       = \"select * from example where param = :value\"\n  )\n\n  // retrieve the parent span from context to use as parent if available.\n  if parentSpan := zipkin.SpanFromContext(ctx); parentSpan != nil {\n    spanContext = parentSpan.Context()\n  }\n\n  // create the remote Zipkin endpoint\n  ep, _ := zipkin.NewEndpoint(serviceName, serviceHost)\n\n  // create a new span to record the resource interaction\n  span := zipkin.StartSpan(\n    queryLabel,\n    zipkin.Parent(parentSpan.Context()),\n    zipkin.WithRemoteEndpoint(ep),\n  )\n\n\t// add interesting key/value pair to our span\n\tspan.SetTag(\"query\", query)\n\n\t// add interesting timed event to our span\n\tspan.Annotate(time.Now(), \"query:start\")\n\n\t// do the actual query...\n\n\t// let's annotate the end...\n\tspan.Annotate(time.Now(), \"query:end\")\n\n\t// we're done with this span.\n\tspan.Finish()\n\n\t// do other stuff\n\t...\n}\n```\n"
  },
  {
    "path": "tracing/zipkin/doc.go",
    "content": "// Package zipkin provides Go kit integration to the OpenZipkin project through\n// the use of zipkin-go, the official OpenZipkin tracer implementation for Go.\n// OpenZipkin is the most used open source distributed tracing ecosystem with\n// many different libraries and interoperability options.\npackage zipkin\n"
  },
  {
    "path": "tracing/zipkin/endpoint.go",
    "content": "package zipkin\n\nimport (\n\t\"context\"\n\n\t\"github.com/openzipkin/zipkin-go\"\n\t\"github.com/openzipkin/zipkin-go/model\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n)\n\n// TraceEndpoint returns an Endpoint middleware, tracing a Go kit endpoint.\n// This endpoint tracer should be used in combination with a Go kit Transport\n// tracing middleware or custom before and after transport functions as\n// propagation of SpanContext is not provided in this middleware.\nfunc TraceEndpoint(tracer *zipkin.Tracer, name string) endpoint.Middleware {\n\treturn func(next endpoint.Endpoint) endpoint.Endpoint {\n\t\treturn func(ctx context.Context, request interface{}) (interface{}, error) {\n\t\t\tvar sc model.SpanContext\n\t\t\tif parentSpan := zipkin.SpanFromContext(ctx); parentSpan != nil {\n\t\t\t\tsc = parentSpan.Context()\n\t\t\t}\n\t\t\tsp := tracer.StartSpan(name, zipkin.Parent(sc))\n\t\t\tdefer sp.Finish()\n\n\t\t\tctx = zipkin.NewContext(ctx, sp)\n\t\t\treturn next(ctx, request)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tracing/zipkin/endpoint_test.go",
    "content": "package zipkin_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/openzipkin/zipkin-go\"\n\t\"github.com/openzipkin/zipkin-go/reporter/recorder\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\tzipkinkit \"github.com/go-kit/kit/tracing/zipkin\"\n)\n\nconst spanName = \"test\"\n\nfunc TestTraceEndpoint(t *testing.T) {\n\trec := recorder.NewReporter()\n\ttr, _ := zipkin.NewTracer(rec)\n\tmw := zipkinkit.TraceEndpoint(tr, spanName)\n\tmw(endpoint.Nop)(context.Background(), nil)\n\n\tspans := rec.Flush()\n\n\tif want, have := 1, len(spans); want != have {\n\t\tt.Fatalf(\"incorrect number of spans, wanted %d, got %d\", want, have)\n\t}\n\n\tif want, have := spanName, spans[0].Name; want != have {\n\t\tt.Fatalf(\"incorrect span name, wanted %s, got %s\", want, have)\n\t}\n}\n"
  },
  {
    "path": "tracing/zipkin/grpc.go",
    "content": "package zipkin\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\n\tzipkin \"github.com/openzipkin/zipkin-go\"\n\t\"github.com/openzipkin/zipkin-go/model\"\n\t\"github.com/openzipkin/zipkin-go/propagation/b3\"\n\t\"google.golang.org/grpc/metadata\"\n\t\"google.golang.org/grpc/status\"\n\n\tkitgrpc \"github.com/go-kit/kit/transport/grpc\"\n\t\"github.com/go-kit/log\"\n)\n\n// GRPCClientTrace enables native Zipkin tracing of a Go kit gRPC transport\n// Client.\n//\n// Go kit creates gRPC transport clients per remote endpoint. This middleware\n// can be set-up individually by adding the endpoint name for each of the Go kit\n// transport clients using the Name() TracerOption.\n// If wanting to use the gRPC FullMethod (/service/method) as Span name you can\n// create a global client tracer omitting the Name() TracerOption, which you can\n// then feed to each Go kit gRPC transport client.\n// If instrumenting a client to an external (not on your platform) service, you\n// will probably want to disallow propagation of SpanContext using the\n// AllowPropagation TracerOption and setting it to false.\nfunc GRPCClientTrace(tracer *zipkin.Tracer, options ...TracerOption) kitgrpc.ClientOption {\n\tconfig := tracerOptions{\n\t\ttags:      make(map[string]string),\n\t\tname:      \"\",\n\t\tlogger:    log.NewNopLogger(),\n\t\tpropagate: true,\n\t}\n\n\tfor _, option := range options {\n\t\toption(&config)\n\t}\n\n\tclientBefore := kitgrpc.ClientBefore(\n\t\tfunc(ctx context.Context, md *metadata.MD) context.Context {\n\t\t\tvar (\n\t\t\t\tspanContext model.SpanContext\n\t\t\t\tname        string\n\t\t\t)\n\n\t\t\tif config.name != \"\" {\n\t\t\t\tname = config.name\n\t\t\t} else {\n\t\t\t\tname = ctx.Value(kitgrpc.ContextKeyRequestMethod).(string)\n\t\t\t}\n\n\t\t\tif parent := zipkin.SpanFromContext(ctx); parent != nil {\n\t\t\t\tspanContext = parent.Context()\n\t\t\t}\n\n\t\t\tspan := tracer.StartSpan(\n\t\t\t\tname,\n\t\t\t\tzipkin.Kind(model.Client),\n\t\t\t\tzipkin.Tags(config.tags),\n\t\t\t\tzipkin.Parent(spanContext),\n\t\t\t\tzipkin.FlushOnFinish(false),\n\t\t\t)\n\n\t\t\tif config.propagate {\n\t\t\t\tif err := b3.InjectGRPC(md)(span.Context()); err != nil {\n\t\t\t\t\tconfig.logger.Log(\"err\", err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn zipkin.NewContext(ctx, span)\n\t\t},\n\t)\n\n\tclientAfter := kitgrpc.ClientAfter(\n\t\tfunc(ctx context.Context, _ metadata.MD, _ metadata.MD) context.Context {\n\t\t\tif span := zipkin.SpanFromContext(ctx); span != nil {\n\t\t\t\tspan.Finish()\n\t\t\t}\n\n\t\t\treturn ctx\n\t\t},\n\t)\n\n\tclientFinalizer := kitgrpc.ClientFinalizer(\n\t\tfunc(ctx context.Context, err error) {\n\t\t\tif span := zipkin.SpanFromContext(ctx); span != nil {\n\t\t\t\tif err != nil {\n\t\t\t\t\tzipkin.TagError.Set(span, err.Error())\n\t\t\t\t}\n\t\t\t\t// calling span.Finish() a second time is a noop, if we didn't get to\n\t\t\t\t// ClientAfter we can at least time the early bail out by calling it\n\t\t\t\t// here.\n\t\t\t\tspan.Finish()\n\t\t\t\t// send span to the Reporter\n\t\t\t\tspan.Flush()\n\t\t\t}\n\t\t},\n\t)\n\n\treturn func(c *kitgrpc.Client) {\n\t\tclientBefore(c)\n\t\tclientAfter(c)\n\t\tclientFinalizer(c)\n\t}\n\n}\n\n// GRPCServerTrace enables native Zipkin tracing of a Go kit gRPC transport\n// Server.\n//\n// Go kit creates gRPC transport servers per gRPC method. This middleware can be\n// set-up individually by adding the method name for each of the Go kit method\n// servers using the Name() TracerOption.\n// If wanting to use the gRPC FullMethod (/service/method) as Span name you can\n// create a global server tracer omitting the Name() TracerOption, which you can\n// then feed to each Go kit method server. For this to work you will need to\n// wire the Go kit gRPC Interceptor too.\n// If instrumenting a service to external (not on your platform) clients, you\n// will probably want to disallow propagation of a client SpanContext using\n// the AllowPropagation TracerOption and setting it to false.\nfunc GRPCServerTrace(tracer *zipkin.Tracer, options ...TracerOption) kitgrpc.ServerOption {\n\tconfig := tracerOptions{\n\t\ttags:      make(map[string]string),\n\t\tname:      \"\",\n\t\tlogger:    log.NewNopLogger(),\n\t\tpropagate: true,\n\t}\n\n\tfor _, option := range options {\n\t\toption(&config)\n\t}\n\n\tserverBefore := kitgrpc.ServerBefore(\n\t\tfunc(ctx context.Context, md metadata.MD) context.Context {\n\t\t\tvar (\n\t\t\t\tspanContext model.SpanContext\n\t\t\t\tname        string\n\t\t\t\ttags        = make(map[string]string)\n\t\t\t)\n\n\t\t\trpcMethod, ok := ctx.Value(kitgrpc.ContextKeyRequestMethod).(string)\n\t\t\tif !ok {\n\t\t\t\tconfig.logger.Log(\"err\", \"unable to retrieve method name: missing gRPC interceptor hook\")\n\t\t\t} else {\n\t\t\t\ttags[\"grpc.method\"] = rpcMethod\n\t\t\t}\n\n\t\t\tif config.name != \"\" {\n\t\t\t\tname = config.name\n\t\t\t} else {\n\t\t\t\tname = rpcMethod\n\t\t\t}\n\n\t\t\tif config.propagate {\n\t\t\t\tspanContext = tracer.Extract(b3.ExtractGRPC(&md))\n\t\t\t\tif spanContext.Err != nil {\n\t\t\t\t\tconfig.logger.Log(\"err\", spanContext.Err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tspan := tracer.StartSpan(\n\t\t\t\tname,\n\t\t\t\tzipkin.Kind(model.Server),\n\t\t\t\tzipkin.Tags(config.tags),\n\t\t\t\tzipkin.Tags(tags),\n\t\t\t\tzipkin.Parent(spanContext),\n\t\t\t\tzipkin.FlushOnFinish(false),\n\t\t\t)\n\n\t\t\treturn zipkin.NewContext(ctx, span)\n\t\t},\n\t)\n\n\tserverAfter := kitgrpc.ServerAfter(\n\t\tfunc(ctx context.Context, _ *metadata.MD, _ *metadata.MD) context.Context {\n\t\t\tif span := zipkin.SpanFromContext(ctx); span != nil {\n\t\t\t\tspan.Finish()\n\t\t\t}\n\n\t\t\treturn ctx\n\t\t},\n\t)\n\n\tserverFinalizer := kitgrpc.ServerFinalizer(\n\t\tfunc(ctx context.Context, err error) {\n\t\t\tif span := zipkin.SpanFromContext(ctx); span != nil {\n\t\t\t\tif err != nil {\n\t\t\t\t\tif status, ok := status.FromError(err); ok {\n\t\t\t\t\t\tstatusCode := strconv.FormatUint(uint64(status.Code()), 10)\n\t\t\t\t\t\tzipkin.TagGRPCStatusCode.Set(span, statusCode)\n\t\t\t\t\t\tzipkin.TagError.Set(span, status.Message())\n\t\t\t\t\t} else {\n\t\t\t\t\t\tzipkin.TagError.Set(span, err.Error())\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// calling span.Finish() a second time is a noop, if we didn't get to\n\t\t\t\t// ServerAfter we can at least time the early bail out by calling it\n\t\t\t\t// here.\n\t\t\t\tspan.Finish()\n\t\t\t\t// send span to the Reporter\n\t\t\t\tspan.Flush()\n\t\t\t}\n\t\t},\n\t)\n\n\treturn func(s *kitgrpc.Server) {\n\t\tserverBefore(s)\n\t\tserverAfter(s)\n\t\tserverFinalizer(s)\n\t}\n}\n"
  },
  {
    "path": "tracing/zipkin/grpc_test.go",
    "content": "package zipkin_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\tzipkin \"github.com/openzipkin/zipkin-go\"\n\t\"github.com/openzipkin/zipkin-go/propagation/b3\"\n\t\"github.com/openzipkin/zipkin-go/reporter/recorder\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/metadata\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\tkitzipkin \"github.com/go-kit/kit/tracing/zipkin\"\n\tgrpctransport \"github.com/go-kit/kit/transport/grpc\"\n)\n\ntype dummy struct{}\n\nfunc unaryInterceptor(\n\tctx context.Context, method string, req, reply interface{},\n\tcc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption,\n) error {\n\treturn nil\n}\n\nfunc TestGRPCClientTrace(t *testing.T) {\n\trec := recorder.NewReporter()\n\tdefer rec.Close()\n\n\ttr, _ := zipkin.NewTracer(rec)\n\n\tclientTracer := kitzipkin.GRPCClientTrace(tr)\n\n\tcc, err := grpc.Dial(\n\t\t\"\",\n\t\tgrpc.WithUnaryInterceptor(unaryInterceptor),\n\t\tgrpc.WithInsecure(),\n\t)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to create gRPC dialer: %s\", err.Error())\n\t}\n\n\tep := grpctransport.NewClient(\n\t\tcc,\n\t\t\"dummyService\",\n\t\t\"dummyMethod\",\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return nil, nil },\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return nil, nil },\n\t\tdummy{},\n\t\tclientTracer,\n\t).Endpoint()\n\n\tparentSpan := tr.StartSpan(\"test\")\n\tctx := zipkin.NewContext(context.Background(), parentSpan)\n\n\tif _, err = ep(ctx, nil); err != nil {\n\t\tt.Errorf(\"unexpected error: %s\", err.Error())\n\t}\n\n\tspans := rec.Flush()\n\tif want, have := 1, len(spans); want != have {\n\t\tt.Fatalf(\"incorrect number of spans, want %d, have %d\", want, have)\n\t}\n\n\tif spans[0].SpanContext.ParentID == nil {\n\t\tt.Fatalf(\"incorrect parent ID, want %s have nil\", parentSpan.Context().ID)\n\t}\n\n\tif want, have := parentSpan.Context().ID, *spans[0].SpanContext.ParentID; want != have {\n\t\tt.Fatalf(\"incorrect parent ID, want %s, have %s\", want, have)\n\t}\n}\n\nfunc TestGRPCServerTrace(t *testing.T) {\n\trec := recorder.NewReporter()\n\tdefer rec.Close()\n\n\ttr, _ := zipkin.NewTracer(rec)\n\n\tserverTracer := kitzipkin.GRPCServerTrace(tr)\n\n\tserver := grpctransport.NewServer(\n\t\tendpoint.Nop,\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return nil, nil },\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return nil, nil },\n\t\tserverTracer,\n\t)\n\n\tmd := metadata.MD{}\n\tparentSpan := tr.StartSpan(\"test\")\n\n\tb3.InjectGRPC(&md)(parentSpan.Context())\n\n\tctx := metadata.NewIncomingContext(context.Background(), md)\n\tserver.ServeGRPC(ctx, nil)\n\n\tspans := rec.Flush()\n\n\tif want, have := 1, len(spans); want != have {\n\t\tt.Fatalf(\"incorrect number of spans, want %d, have %d\", want, have)\n\t}\n\n\tif want, have := parentSpan.Context().TraceID, spans[0].SpanContext.TraceID; want != have {\n\t\tt.Errorf(\"incorrect TraceID, want %+v, have %+v\", want, have)\n\t}\n\n\tif want, have := parentSpan.Context().ID, spans[0].SpanContext.ID; want != have {\n\t\tt.Errorf(\"incorrect span ID, want %d, have %d\", want, have)\n\t}\n}\n"
  },
  {
    "path": "tracing/zipkin/http.go",
    "content": "package zipkin\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"strconv\"\n\n\tzipkin \"github.com/openzipkin/zipkin-go\"\n\t\"github.com/openzipkin/zipkin-go/model\"\n\t\"github.com/openzipkin/zipkin-go/propagation/b3\"\n\n\tkithttp \"github.com/go-kit/kit/transport/http\"\n\t\"github.com/go-kit/log\"\n)\n\n// HTTPClientTrace enables native Zipkin tracing of a Go kit HTTP transport\n// Client.\n//\n// Go kit creates HTTP transport clients per remote endpoint. This middleware\n// can be set-up individually by adding the endpoint name for each of the Go kit\n// transport clients using the Name() TracerOption.\n// If wanting to use the HTTP Method (Get, Post, Put, etc.) as Span name you can\n// create a global client tracer omitting the Name() TracerOption, which you can\n// then feed to each Go kit transport client.\n// If instrumenting a client to an external (not on your platform) service, you\n// will probably want to disallow propagation of SpanContext using the\n// AllowPropagation TracerOption and setting it to false.\nfunc HTTPClientTrace(tracer *zipkin.Tracer, options ...TracerOption) kithttp.ClientOption {\n\tconfig := tracerOptions{\n\t\ttags:      make(map[string]string),\n\t\tname:      \"\",\n\t\tlogger:    log.NewNopLogger(),\n\t\tpropagate: true,\n\t}\n\n\tfor _, option := range options {\n\t\toption(&config)\n\t}\n\n\tclientBefore := kithttp.ClientBefore(\n\t\tfunc(ctx context.Context, req *http.Request) context.Context {\n\t\t\tvar (\n\t\t\t\tspanContext model.SpanContext\n\t\t\t\tname        string\n\t\t\t)\n\n\t\t\tif config.name != \"\" {\n\t\t\t\tname = config.name\n\t\t\t} else {\n\t\t\t\tname = req.Method\n\t\t\t}\n\n\t\t\tif parent := zipkin.SpanFromContext(ctx); parent != nil {\n\t\t\t\tspanContext = parent.Context()\n\t\t\t}\n\n\t\t\ttags := map[string]string{\n\t\t\t\tstring(zipkin.TagHTTPMethod): req.Method,\n\t\t\t\tstring(zipkin.TagHTTPUrl):    req.URL.String(),\n\t\t\t}\n\n\t\t\tspan := tracer.StartSpan(\n\t\t\t\tname,\n\t\t\t\tzipkin.Kind(model.Client),\n\t\t\t\tzipkin.Tags(config.tags),\n\t\t\t\tzipkin.Tags(tags),\n\t\t\t\tzipkin.Parent(spanContext),\n\t\t\t\tzipkin.FlushOnFinish(false),\n\t\t\t)\n\n\t\t\tif config.propagate {\n\t\t\t\tif err := b3.InjectHTTP(req)(span.Context()); err != nil {\n\t\t\t\t\tconfig.logger.Log(\"err\", err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn zipkin.NewContext(ctx, span)\n\t\t},\n\t)\n\n\tclientAfter := kithttp.ClientAfter(\n\t\tfunc(ctx context.Context, res *http.Response) context.Context {\n\t\t\tif span := zipkin.SpanFromContext(ctx); span != nil {\n\t\t\t\tzipkin.TagHTTPResponseSize.Set(span, strconv.FormatInt(res.ContentLength, 10))\n\t\t\t\tzipkin.TagHTTPStatusCode.Set(span, strconv.Itoa(res.StatusCode))\n\t\t\t\tif res.StatusCode > 399 {\n\t\t\t\t\tzipkin.TagError.Set(span, strconv.Itoa(res.StatusCode))\n\t\t\t\t}\n\t\t\t\tspan.Finish()\n\t\t\t}\n\n\t\t\treturn ctx\n\t\t},\n\t)\n\n\tclientFinalizer := kithttp.ClientFinalizer(\n\t\tfunc(ctx context.Context, err error) {\n\t\t\tif span := zipkin.SpanFromContext(ctx); span != nil {\n\t\t\t\tif err != nil {\n\t\t\t\t\tzipkin.TagError.Set(span, err.Error())\n\t\t\t\t}\n\t\t\t\t// calling span.Finish() a second time is a noop, if we didn't get to\n\t\t\t\t// ClientAfter we can at least time the early bail out by calling it\n\t\t\t\t// here.\n\t\t\t\tspan.Finish()\n\t\t\t\t// send span to the Reporter\n\t\t\t\tspan.Flush()\n\t\t\t}\n\t\t},\n\t)\n\n\treturn func(c *kithttp.Client) {\n\t\tclientBefore(c)\n\t\tclientAfter(c)\n\t\tclientFinalizer(c)\n\t}\n}\n\n// HTTPServerTrace enables native Zipkin tracing of a Go kit HTTP transport\n// Server.\n//\n// Go kit creates HTTP transport servers per HTTP endpoint. This middleware can\n// be set-up individually by adding the method name for each of the Go kit\n// method servers using the Name() TracerOption.\n// If wanting to use the HTTP method (Get, Post, Put, etc.) as Span name you can\n// create a global server tracer omitting the Name() TracerOption, which you can\n// then feed to each Go kit method server.\n//\n// If instrumenting a service to external (not on your platform) clients, you\n// will probably want to disallow propagation of a client SpanContext using\n// the AllowPropagation TracerOption and setting it to false.\nfunc HTTPServerTrace(tracer *zipkin.Tracer, options ...TracerOption) kithttp.ServerOption {\n\tconfig := tracerOptions{\n\t\ttags:      make(map[string]string),\n\t\tname:      \"\",\n\t\tlogger:    log.NewNopLogger(),\n\t\tpropagate: true,\n\t}\n\n\tfor _, option := range options {\n\t\toption(&config)\n\t}\n\n\tserverBefore := kithttp.ServerBefore(\n\t\tfunc(ctx context.Context, req *http.Request) context.Context {\n\t\t\tvar (\n\t\t\t\tspanContext model.SpanContext\n\t\t\t\tname        string\n\t\t\t)\n\n\t\t\tif config.name != \"\" {\n\t\t\t\tname = config.name\n\t\t\t} else {\n\t\t\t\tname = req.Method\n\t\t\t}\n\n\t\t\tif config.propagate {\n\t\t\t\tspanContext = tracer.Extract(b3.ExtractHTTP(req))\n\n\t\t\t\tif spanContext.Sampled == nil && config.requestSampler != nil {\n\t\t\t\t\tsample := config.requestSampler(req)\n\t\t\t\t\tspanContext.Sampled = &sample\n\t\t\t\t}\n\n\t\t\t\tif spanContext.Err != nil {\n\t\t\t\t\tconfig.logger.Log(\"err\", spanContext.Err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttags := map[string]string{\n\t\t\t\tstring(zipkin.TagHTTPMethod): req.Method,\n\t\t\t\tstring(zipkin.TagHTTPPath):   req.URL.Path,\n\t\t\t}\n\n\t\t\tspan := tracer.StartSpan(\n\t\t\t\tname,\n\t\t\t\tzipkin.Kind(model.Server),\n\t\t\t\tzipkin.Tags(config.tags),\n\t\t\t\tzipkin.Tags(tags),\n\t\t\t\tzipkin.Parent(spanContext),\n\t\t\t\tzipkin.FlushOnFinish(false),\n\t\t\t)\n\n\t\t\treturn zipkin.NewContext(ctx, span)\n\t\t},\n\t)\n\n\tserverAfter := kithttp.ServerAfter(\n\t\tfunc(ctx context.Context, _ http.ResponseWriter) context.Context {\n\t\t\tif span := zipkin.SpanFromContext(ctx); span != nil {\n\t\t\t\tspan.Finish()\n\t\t\t}\n\n\t\t\treturn ctx\n\t\t},\n\t)\n\n\tserverFinalizer := kithttp.ServerFinalizer(\n\t\tfunc(ctx context.Context, code int, r *http.Request) {\n\t\t\tif span := zipkin.SpanFromContext(ctx); span != nil {\n\t\t\t\tzipkin.TagHTTPStatusCode.Set(span, strconv.Itoa(code))\n\t\t\t\tif code > 399 {\n\t\t\t\t\t// set http status as error tag (if already set, this is a noop)\n\t\t\t\t\tzipkin.TagError.Set(span, http.StatusText(code))\n\t\t\t\t}\n\t\t\t\tif rs, ok := ctx.Value(kithttp.ContextKeyResponseSize).(int64); ok {\n\t\t\t\t\tzipkin.TagHTTPResponseSize.Set(span, strconv.FormatInt(rs, 10))\n\t\t\t\t}\n\n\t\t\t\t// calling span.Finish() a second time is a noop, if we didn't get to\n\t\t\t\t// ServerAfter we can at least time the early bail out by calling it\n\t\t\t\t// here.\n\t\t\t\tspan.Finish()\n\t\t\t\t// send span to the Reporter\n\t\t\t\tspan.Flush()\n\t\t\t}\n\t\t},\n\t)\n\n\treturn func(s *kithttp.Server) {\n\t\tserverBefore(s)\n\t\tserverAfter(s)\n\t\tserverFinalizer(s)\n\t}\n}\n"
  },
  {
    "path": "tracing/zipkin/http_test.go",
    "content": "package zipkin_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"testing\"\n\n\tzipkin \"github.com/openzipkin/zipkin-go\"\n\t\"github.com/openzipkin/zipkin-go/model\"\n\t\"github.com/openzipkin/zipkin-go/propagation/b3\"\n\t\"github.com/openzipkin/zipkin-go/reporter/recorder\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\tzipkinkit \"github.com/go-kit/kit/tracing/zipkin\"\n\tkithttp \"github.com/go-kit/kit/transport/http\"\n)\n\nconst (\n\ttestName     = \"test\"\n\ttestBody     = \"test_body\"\n\ttestTagKey   = \"test_key\"\n\ttestTagValue = \"test_value\"\n)\n\nfunc TestHTTPClientTracePropagatesParentSpan(t *testing.T) {\n\trec := recorder.NewReporter()\n\tdefer rec.Close()\n\n\ttr, _ := zipkin.NewTracer(rec)\n\n\trURL, _ := url.Parse(\"https://httpbin.org/get\")\n\n\tclientTracer := zipkinkit.HTTPClientTrace(tr)\n\tep := kithttp.NewClient(\n\t\t\"GET\",\n\t\trURL,\n\t\tfunc(ctx context.Context, r *http.Request, i interface{}) error {\n\t\t\treturn nil\n\t\t},\n\t\tfunc(ctx context.Context, r *http.Response) (response interface{}, err error) {\n\t\t\treturn nil, nil\n\t\t},\n\t\tclientTracer,\n\t).Endpoint()\n\n\tparentSpan := tr.StartSpan(\"test\")\n\n\tctx := zipkin.NewContext(context.Background(), parentSpan)\n\n\t_, err := ep(ctx, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error: %s\", err.Error())\n\t}\n\n\tspans := rec.Flush()\n\tif want, have := 1, len(spans); want != have {\n\t\tt.Fatalf(\"incorrect number of spans, want %d, have %d\", want, have)\n\t}\n\n\tspan := spans[0]\n\tif span.SpanContext.ParentID == nil {\n\t\tt.Fatalf(\"incorrect parent ID, want %s have nil\", parentSpan.Context().ID)\n\t}\n\n\tif want, have := parentSpan.Context().ID, *span.SpanContext.ParentID; want != have {\n\t\tt.Fatalf(\"incorrect parent ID, want %s, have %s\", want, have)\n\t}\n}\n\nfunc TestHTTPClientTraceAddsExpectedTags(t *testing.T) {\n\tdataProvider := []struct {\n\t\tResponseStatusCode int\n\t\tErrorTagValue      string\n\t}{\n\t\t{http.StatusOK, \"\"},\n\t\t{http.StatusForbidden, fmt.Sprint(http.StatusForbidden)},\n\t}\n\n\tfor _, data := range dataProvider {\n\t\ttestHTTPClientTraceCase(t, data.ResponseStatusCode, data.ErrorTagValue)\n\t}\n}\n\nfunc testHTTPClientTraceCase(t *testing.T, responseStatusCode int, errTagValue string) {\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(responseStatusCode)\n\t\tw.Write([]byte(testBody))\n\t}))\n\tdefer ts.Close()\n\n\trec := recorder.NewReporter()\n\tdefer rec.Close()\n\n\ttr, err := zipkin.NewTracer(rec)\n\tif err != nil {\n\t\tt.Errorf(\"Unwanted error: %s\", err.Error())\n\t}\n\n\trMethod := \"GET\"\n\trURL, _ := url.Parse(ts.URL)\n\n\tclientTracer := zipkinkit.HTTPClientTrace(\n\t\ttr,\n\t\tzipkinkit.Name(testName),\n\t\tzipkinkit.Tags(map[string]string{testTagKey: testTagValue}),\n\t)\n\n\tep := kithttp.NewClient(\n\t\trMethod,\n\t\trURL,\n\t\tfunc(ctx context.Context, r *http.Request, i interface{}) error {\n\t\t\treturn nil\n\t\t},\n\t\tfunc(ctx context.Context, r *http.Response) (response interface{}, err error) {\n\t\t\treturn nil, nil\n\t\t},\n\t\tclientTracer,\n\t).Endpoint()\n\n\t_, err = ep(context.Background(), nil)\n\tif err != nil {\n\t\tt.Fatalf(\"unwanted error: %s\", err.Error())\n\t}\n\n\tspans := rec.Flush()\n\tif want, have := 1, len(spans); want != have {\n\t\tt.Fatalf(\"incorrect number of spans, wanted %d, got %d\", want, have)\n\t}\n\n\tspan := spans[0]\n\tif span.SpanContext.ParentID != nil {\n\t\tt.Fatalf(\"incorrect parentID, wanted nil, got %s\", span.SpanContext.ParentID)\n\t}\n\n\tif want, have := testName, span.Name; want != have {\n\t\tt.Fatalf(\"incorrect span name, wanted %s, got %s\", want, have)\n\t}\n\n\tif want, have := model.Client, span.Kind; want != have {\n\t\tt.Fatalf(\"incorrect span kind, wanted %s, got %s\", want, have)\n\t}\n\n\ttags := map[string]string{\n\t\ttestTagKey:                         testTagValue,\n\t\tstring(zipkin.TagHTTPStatusCode):   fmt.Sprint(responseStatusCode),\n\t\tstring(zipkin.TagHTTPMethod):       rMethod,\n\t\tstring(zipkin.TagHTTPUrl):          rURL.String(),\n\t\tstring(zipkin.TagHTTPResponseSize): fmt.Sprint(len(testBody)),\n\t}\n\n\tif errTagValue != \"\" {\n\t\ttags[string(zipkin.TagError)] = fmt.Sprint(errTagValue)\n\t}\n\n\tif !reflect.DeepEqual(span.Tags, tags) {\n\t\tt.Fatalf(\"invalid tags set, wanted %+v, got %+v\", tags, span.Tags)\n\t}\n}\n\nfunc TestHTTPServerTrace(t *testing.T) {\n\trec := recorder.NewReporter()\n\tdefer rec.Close()\n\n\t// explicitly show we use the default of RPC shared spans in Zipkin as it\n\t// is idiomatic for Zipkin to share span identifiers between client and\n\t// server side.\n\ttr, _ := zipkin.NewTracer(rec, zipkin.WithSharedSpans(true))\n\n\thandler := kithttp.NewServer(\n\t\tendpoint.Nop,\n\t\tfunc(context.Context, *http.Request) (interface{}, error) { return nil, nil },\n\t\tfunc(context.Context, http.ResponseWriter, interface{}) error { return errors.New(\"dummy\") },\n\t\tzipkinkit.HTTPServerTrace(tr),\n\t)\n\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\n\tconst httpMethod = \"GET\"\n\n\treq, err := http.NewRequest(httpMethod, server.URL, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to create HTTP request: %s\", err.Error())\n\t}\n\n\tparentSpan := tr.StartSpan(\"Dummy\")\n\n\tb3.InjectHTTP(req)(parentSpan.Context())\n\n\tclient := http.Client{}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to send HTTP request: %s\", err.Error())\n\t}\n\tresp.Body.Close()\n\n\tspans := rec.Flush()\n\tif want, have := 1, len(spans); want != have {\n\t\tt.Fatalf(\"incorrect number of spans, want %d, have %d\", want, have)\n\t}\n\n\tif want, have := parentSpan.Context().TraceID, spans[0].SpanContext.TraceID; want != have {\n\t\tt.Errorf(\"incorrect TraceID, want %+v, have %+v\", want, have)\n\t}\n\n\tif want, have := parentSpan.Context().ID, spans[0].SpanContext.ID; want != have {\n\t\tt.Errorf(\"incorrect span ID, want %d, have %d\", want, have)\n\t}\n\n\tif want, have := httpMethod, spans[0].Name; want != have {\n\t\tt.Errorf(\"incorrect span name, want %s, have %s\", want, have)\n\t}\n\n\tif want, have := http.StatusText(500), spans[0].Tags[\"error\"]; want != have {\n\t\tt.Fatalf(\"incorrect error tag, want %s, have %s\", want, have)\n\t}\n}\n\nfunc TestHTTPServerTraceIsRequestBasedSampled(t *testing.T) {\n\trec := recorder.NewReporter()\n\tdefer rec.Close()\n\n\tconst httpMethod = \"DELETE\"\n\n\ttr, _ := zipkin.NewTracer(rec)\n\n\thandler := kithttp.NewServer(\n\t\tendpoint.Nop,\n\t\tfunc(context.Context, *http.Request) (interface{}, error) { return nil, nil },\n\t\tfunc(context.Context, http.ResponseWriter, interface{}) error { return nil },\n\t\tzipkinkit.HTTPServerTrace(tr, zipkinkit.RequestSampler(func(r *http.Request) bool {\n\t\t\treturn r.Method == httpMethod\n\t\t})),\n\t)\n\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\n\treq, err := http.NewRequest(httpMethod, server.URL, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to create HTTP request: %s\", err.Error())\n\t}\n\n\tclient := http.Client{}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to send HTTP request: %s\", err.Error())\n\t}\n\tresp.Body.Close()\n\n\tspans := rec.Flush()\n\tif want, have := 1, len(spans); want != have {\n\t\tt.Fatalf(\"incorrect number of spans, want %d, have %d\", want, have)\n\t}\n}\n"
  },
  {
    "path": "tracing/zipkin/options.go",
    "content": "package zipkin\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/go-kit/log\"\n)\n\n// TracerOption allows for functional options to our Zipkin tracing middleware.\ntype TracerOption func(o *tracerOptions)\n\n// Name sets the name for an instrumented transport endpoint. If name is omitted\n// at tracing middleware creation, the method of the transport or transport rpc\n// name is used.\nfunc Name(name string) TracerOption {\n\treturn func(o *tracerOptions) {\n\t\to.name = name\n\t}\n}\n\n// Tags adds default tags to our Zipkin transport spans.\nfunc Tags(tags map[string]string) TracerOption {\n\treturn func(o *tracerOptions) {\n\t\tfor k, v := range tags {\n\t\t\to.tags[k] = v\n\t\t}\n\t}\n}\n\n// Logger adds a Go kit logger to our Zipkin Middleware to log SpanContext\n// extract / inject errors if they occur. Default is Noop.\nfunc Logger(logger log.Logger) TracerOption {\n\treturn func(o *tracerOptions) {\n\t\tif logger != nil {\n\t\t\to.logger = logger\n\t\t}\n\t}\n}\n\n// AllowPropagation instructs the tracer to allow or deny propagation of the\n// span context between this instrumented client or service and its peers. If\n// the instrumented client connects to services outside its own platform or if\n// the instrumented service receives requests from untrusted clients it is\n// strongly advised to disallow propagation. Propagation between services inside\n// your own platform benefit from propagation. Default for both TraceClient and\n// TraceServer is to allow propagation.\nfunc AllowPropagation(propagate bool) TracerOption {\n\treturn func(o *tracerOptions) {\n\t\to.propagate = propagate\n\t}\n}\n\n// RequestSampler allows one to set the sampling decision based on the details\n// found in the http.Request.\nfunc RequestSampler(sampleFunc func(r *http.Request) bool) TracerOption {\n\treturn func(o *tracerOptions) {\n\t\to.requestSampler = sampleFunc\n\t}\n}\n\ntype tracerOptions struct {\n\ttags           map[string]string\n\tname           string\n\tlogger         log.Logger\n\tpropagate      bool\n\trequestSampler func(r *http.Request) bool\n}\n"
  },
  {
    "path": "transport/amqp/doc.go",
    "content": "// Package amqp implements an AMQP transport.\npackage amqp\n"
  },
  {
    "path": "transport/amqp/encode_decode.go",
    "content": "package amqp\n\nimport (\n\t\"context\"\n\n\tamqp \"github.com/rabbitmq/amqp091-go\"\n)\n\n// DecodeRequestFunc extracts a user-domain request object from\n// an AMQP Delivery object. It is designed to be used in AMQP Subscribers.\ntype DecodeRequestFunc func(context.Context, *amqp.Delivery) (request interface{}, err error)\n\n// EncodeRequestFunc encodes the passed request object into\n// an AMQP Publishing object. It is designed to be used in AMQP Publishers.\ntype EncodeRequestFunc func(context.Context, *amqp.Publishing, interface{}) error\n\n// EncodeResponseFunc encodes the passed response object to\n// an AMQP Publishing object. It is designed to be used in AMQP Subscribers.\ntype EncodeResponseFunc func(context.Context, *amqp.Publishing, interface{}) error\n\n// DecodeResponseFunc extracts a user-domain response object from\n// an AMQP Delivery object. It is designed to be used in AMQP Publishers.\ntype DecodeResponseFunc func(context.Context, *amqp.Delivery) (response interface{}, err error)\n"
  },
  {
    "path": "transport/amqp/publisher.go",
    "content": "package amqp\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\tamqp \"github.com/rabbitmq/amqp091-go\"\n)\n\n// The golang AMQP implementation requires the []byte representation of\n// correlation id strings to have a maximum length of 255 bytes.\nconst maxCorrelationIdLength = 255\n\n// Publisher wraps an AMQP channel and queue, and provides a method that\n// implements endpoint.Endpoint.\ntype Publisher struct {\n\tch        Channel\n\tq         *amqp.Queue\n\tenc       EncodeRequestFunc\n\tdec       DecodeResponseFunc\n\tbefore    []RequestFunc\n\tafter     []PublisherResponseFunc\n\tdeliverer Deliverer\n\ttimeout   time.Duration\n}\n\n// NewPublisher constructs a usable Publisher for a single remote method.\nfunc NewPublisher(\n\tch Channel,\n\tq *amqp.Queue,\n\tenc EncodeRequestFunc,\n\tdec DecodeResponseFunc,\n\toptions ...PublisherOption,\n) *Publisher {\n\tp := &Publisher{\n\t\tch:        ch,\n\t\tq:         q,\n\t\tenc:       enc,\n\t\tdec:       dec,\n\t\tdeliverer: DefaultDeliverer,\n\t\ttimeout:   10 * time.Second,\n\t}\n\tfor _, option := range options {\n\t\toption(p)\n\t}\n\treturn p\n}\n\n// PublisherOption sets an optional parameter for clients.\ntype PublisherOption func(*Publisher)\n\n// PublisherBefore sets the RequestFuncs that are applied to the outgoing AMQP\n// request before it's invoked.\nfunc PublisherBefore(before ...RequestFunc) PublisherOption {\n\treturn func(p *Publisher) { p.before = append(p.before, before...) }\n}\n\n// PublisherAfter sets the ClientResponseFuncs applied to the incoming AMQP\n// request prior to it being decoded. This is useful for obtaining anything off\n// of the response and adding onto the context prior to decoding.\nfunc PublisherAfter(after ...PublisherResponseFunc) PublisherOption {\n\treturn func(p *Publisher) { p.after = append(p.after, after...) }\n}\n\n// PublisherDeliverer sets the deliverer function that the Publisher invokes.\nfunc PublisherDeliverer(deliverer Deliverer) PublisherOption {\n\treturn func(p *Publisher) { p.deliverer = deliverer }\n}\n\n// PublisherTimeout sets the available timeout for an AMQP request.\nfunc PublisherTimeout(timeout time.Duration) PublisherOption {\n\treturn func(p *Publisher) { p.timeout = timeout }\n}\n\n// Endpoint returns a usable endpoint that invokes the remote endpoint.\nfunc (p Publisher) Endpoint() endpoint.Endpoint {\n\treturn func(ctx context.Context, request interface{}) (interface{}, error) {\n\t\tctx, cancel := context.WithTimeout(ctx, p.timeout)\n\t\tdefer cancel()\n\n\t\tpub := amqp.Publishing{\n\t\t\tReplyTo:       p.q.Name,\n\t\t\tCorrelationId: randomString(randInt(5, maxCorrelationIdLength)),\n\t\t}\n\n\t\tif err := p.enc(ctx, &pub, request); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tfor _, f := range p.before {\n\t\t\t// Affect only amqp.Publishing\n\t\t\tctx = f(ctx, &pub, nil)\n\t\t}\n\n\t\tdeliv, err := p.deliverer(ctx, p, &pub)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tfor _, f := range p.after {\n\t\t\tctx = f(ctx, deliv)\n\t\t}\n\t\tresponse, err := p.dec(ctx, deliv)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn response, nil\n\t}\n}\n\n// Deliverer is invoked by the Publisher to publish the specified Publishing, and to\n// retrieve the appropriate response Delivery object.\ntype Deliverer func(\n\tcontext.Context,\n\tPublisher,\n\t*amqp.Publishing,\n) (*amqp.Delivery, error)\n\n// DefaultDeliverer is a deliverer that publishes the specified Publishing\n// and returns the first Delivery object with the matching correlationId.\n// If the context times out while waiting for a reply, an error will be returned.\nfunc DefaultDeliverer(\n\tctx context.Context,\n\tp Publisher,\n\tpub *amqp.Publishing,\n) (*amqp.Delivery, error) {\n\terr := p.ch.Publish(\n\t\tgetPublishExchange(ctx),\n\t\tgetPublishKey(ctx),\n\t\tfalse, //mandatory\n\t\tfalse, //immediate\n\t\t*pub,\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tautoAck := getConsumeAutoAck(ctx)\n\n\tmsg, err := p.ch.Consume(\n\t\tp.q.Name,\n\t\t\"\", //consumer\n\t\tautoAck,\n\t\tfalse, //exclusive\n\t\tfalse, //noLocal\n\t\tfalse, //noWait\n\t\tgetConsumeArgs(ctx),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor {\n\t\tselect {\n\t\tcase d := <-msg:\n\t\t\tif d.CorrelationId == pub.CorrelationId {\n\t\t\t\tif !autoAck {\n\t\t\t\t\td.Ack(false) //multiple\n\t\t\t\t}\n\t\t\t\treturn &d, nil\n\t\t\t}\n\n\t\tcase <-ctx.Done():\n\t\t\treturn nil, ctx.Err()\n\t\t}\n\t}\n\n}\n\n// SendAndForgetDeliverer delivers the supplied publishing and\n// returns a nil response.\n// When using this deliverer please ensure that the supplied DecodeResponseFunc and\n// PublisherResponseFunc are able to handle nil-type responses.\nfunc SendAndForgetDeliverer(\n\tctx context.Context,\n\tp Publisher,\n\tpub *amqp.Publishing,\n) (*amqp.Delivery, error) {\n\terr := p.ch.Publish(\n\t\tgetPublishExchange(ctx),\n\t\tgetPublishKey(ctx),\n\t\tfalse, //mandatory\n\t\tfalse, //immediate\n\t\t*pub,\n\t)\n\treturn nil, err\n}\n"
  },
  {
    "path": "transport/amqp/publisher_test.go",
    "content": "package amqp_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\tamqptransport \"github.com/go-kit/kit/transport/amqp\"\n\tamqp \"github.com/rabbitmq/amqp091-go\"\n)\n\nvar (\n\tdefaultContentType     = \"\"\n\tdefaultContentEncoding = \"\"\n)\n\n// TestBadEncode tests if encode errors are handled properly.\nfunc TestBadEncode(t *testing.T) {\n\tch := &mockChannel{f: nullFunc}\n\tq := &amqp.Queue{Name: \"some queue\"}\n\tpub := amqptransport.NewPublisher(\n\t\tch,\n\t\tq,\n\t\tfunc(context.Context, *amqp.Publishing, interface{}) error { return errors.New(\"err!\") },\n\t\tfunc(context.Context, *amqp.Delivery) (response interface{}, err error) { return struct{}{}, nil },\n\t)\n\terrChan := make(chan error, 1)\n\tvar err error\n\tgo func() {\n\t\t_, err := pub.Endpoint()(context.Background(), struct{}{})\n\t\terrChan <- err\n\n\t}()\n\tselect {\n\tcase err = <-errChan:\n\t\tbreak\n\n\tcase <-time.After(100 * time.Millisecond):\n\t\tt.Fatal(\"Timed out waiting for result\")\n\t}\n\tif err == nil {\n\t\tt.Error(\"expected error\")\n\t}\n\tif want, have := \"err!\", err.Error(); want != have {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t}\n}\n\n// TestBadDecode tests if decode errors are handled properly.\nfunc TestBadDecode(t *testing.T) {\n\tcid := \"correlation\"\n\tch := &mockChannel{\n\t\tf: nullFunc,\n\t\tc: make(chan amqp.Publishing, 1),\n\t\tdeliveries: []amqp.Delivery{\n\t\t\tamqp.Delivery{\n\t\t\t\tCorrelationId: cid,\n\t\t\t},\n\t\t},\n\t}\n\tq := &amqp.Queue{Name: \"some queue\"}\n\n\tpub := amqptransport.NewPublisher(\n\t\tch,\n\t\tq,\n\t\tfunc(context.Context, *amqp.Publishing, interface{}) error { return nil },\n\t\tfunc(context.Context, *amqp.Delivery) (response interface{}, err error) {\n\t\t\treturn struct{}{}, errors.New(\"err!\")\n\t\t},\n\t\tamqptransport.PublisherBefore(\n\t\t\tamqptransport.SetCorrelationID(cid),\n\t\t),\n\t)\n\n\tvar err error\n\terrChan := make(chan error, 1)\n\tgo func() {\n\t\t_, err := pub.Endpoint()(context.Background(), struct{}{})\n\t\terrChan <- err\n\n\t}()\n\n\tselect {\n\tcase err = <-errChan:\n\t\tbreak\n\n\tcase <-time.After(100 * time.Millisecond):\n\t\tt.Fatal(\"Timed out waiting for result\")\n\t}\n\n\tif err == nil {\n\t\tt.Error(\"expected error\")\n\t}\n\tif want, have := \"err!\", err.Error(); want != have {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t}\n}\n\n// TestPublisherTimeout ensures that the publisher timeout mechanism works.\nfunc TestPublisherTimeout(t *testing.T) {\n\tch := &mockChannel{\n\t\tf:          nullFunc,\n\t\tc:          make(chan amqp.Publishing, 1),\n\t\tdeliveries: []amqp.Delivery{}, // no reply from mock subscriber\n\t}\n\tq := &amqp.Queue{Name: \"some queue\"}\n\n\tpub := amqptransport.NewPublisher(\n\t\tch,\n\t\tq,\n\t\tfunc(context.Context, *amqp.Publishing, interface{}) error { return nil },\n\t\tfunc(context.Context, *amqp.Delivery) (response interface{}, err error) {\n\t\t\treturn struct{}{}, nil\n\t\t},\n\t\tamqptransport.PublisherTimeout(50*time.Millisecond),\n\t)\n\n\tvar err error\n\terrChan := make(chan error, 1)\n\tgo func() {\n\t\t_, err := pub.Endpoint()(context.Background(), struct{}{})\n\t\terrChan <- err\n\n\t}()\n\n\tselect {\n\tcase err = <-errChan:\n\t\tbreak\n\n\tcase <-time.After(100 * time.Millisecond):\n\t\tt.Fatal(\"timed out waiting for result\")\n\t}\n\n\tif err == nil {\n\t\tt.Error(\"expected error\")\n\t}\n\tif want, have := context.DeadlineExceeded.Error(), err.Error(); want != have {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t}\n}\n\nfunc TestSuccessfulPublisher(t *testing.T) {\n\tcid := \"correlation\"\n\tmockReq := testReq{437}\n\tmockRes := testRes{\n\t\tSquadron: mockReq.Squadron,\n\t\tName:     names[mockReq.Squadron],\n\t}\n\tb, err := json.Marshal(mockRes)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\treqChan := make(chan amqp.Publishing, 1)\n\tch := &mockChannel{\n\t\tf: nullFunc,\n\t\tc: reqChan,\n\t\tdeliveries: []amqp.Delivery{\n\t\t\tamqp.Delivery{\n\t\t\t\tCorrelationId: cid,\n\t\t\t\tBody:          b,\n\t\t\t},\n\t\t},\n\t}\n\tq := &amqp.Queue{Name: \"some queue\"}\n\n\tpub := amqptransport.NewPublisher(\n\t\tch,\n\t\tq,\n\t\ttestReqEncoder,\n\t\ttestResDeliveryDecoder,\n\t\tamqptransport.PublisherBefore(\n\t\t\tamqptransport.SetCorrelationID(cid),\n\t\t),\n\t)\n\tvar publishing amqp.Publishing\n\tvar res testRes\n\tvar ok bool\n\tresChan := make(chan interface{}, 1)\n\terrChan := make(chan error, 1)\n\tgo func() {\n\t\tres, err := pub.Endpoint()(context.Background(), mockReq)\n\t\tif err != nil {\n\t\t\terrChan <- err\n\t\t} else {\n\t\t\tresChan <- res\n\t\t}\n\t}()\n\n\tselect {\n\tcase publishing = <-reqChan:\n\t\tbreak\n\n\tcase <-time.After(100 * time.Millisecond):\n\t\tt.Fatal(\"timed out waiting for request\")\n\t}\n\tif want, have := defaultContentType, publishing.ContentType; want != have {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t}\n\tif want, have := defaultContentEncoding, publishing.ContentEncoding; want != have {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t}\n\n\tselect {\n\tcase response := <-resChan:\n\t\tres, ok = response.(testRes)\n\t\tif !ok {\n\t\t\tt.Error(\"failed to assert endpoint response type\")\n\t\t}\n\t\tbreak\n\n\tcase err = <-errChan:\n\t\tbreak\n\n\tcase <-time.After(100 * time.Millisecond):\n\t\tt.Fatal(\"timed out waiting for result\")\n\t}\n\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif want, have := mockRes.Name, res.Name; want != have {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t}\n}\n\n// TestSendAndForgetPublisher tests that the SendAndForgetDeliverer is working\nfunc TestSendAndForgetPublisher(t *testing.T) {\n\tch := &mockChannel{\n\t\tf:          nullFunc,\n\t\tc:          make(chan amqp.Publishing, 1),\n\t\tdeliveries: []amqp.Delivery{}, // no reply from mock subscriber\n\t}\n\tq := &amqp.Queue{Name: \"some queue\"}\n\n\tpub := amqptransport.NewPublisher(\n\t\tch,\n\t\tq,\n\t\tfunc(context.Context, *amqp.Publishing, interface{}) error { return nil },\n\t\tfunc(context.Context, *amqp.Delivery) (response interface{}, err error) {\n\t\t\treturn struct{}{}, nil\n\t\t},\n\t\tamqptransport.PublisherDeliverer(amqptransport.SendAndForgetDeliverer),\n\t\tamqptransport.PublisherTimeout(50*time.Millisecond),\n\t)\n\n\tvar err error\n\terrChan := make(chan error, 1)\n\tfinishChan := make(chan bool, 1)\n\tgo func() {\n\t\t_, err := pub.Endpoint()(context.Background(), struct{}{})\n\t\tif err != nil {\n\t\t\terrChan <- err\n\t\t} else {\n\t\t\tfinishChan <- true\n\t\t}\n\n\t}()\n\n\tselect {\n\tcase <-finishChan:\n\t\tbreak\n\tcase err = <-errChan:\n\t\tt.Errorf(\"unexpected error %s\", err)\n\tcase <-time.After(100 * time.Millisecond):\n\t\tt.Fatal(\"timed out waiting for result\")\n\t}\n\n}\n"
  },
  {
    "path": "transport/amqp/request_response_func.go",
    "content": "package amqp\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\tamqp \"github.com/rabbitmq/amqp091-go\"\n)\n\n// RequestFunc may take information from a publisher request and put it into a\n// request context. In Subscribers, RequestFuncs are executed prior to invoking\n// the endpoint.\ntype RequestFunc func(context.Context, *amqp.Publishing, *amqp.Delivery) context.Context\n\n// SubscriberResponseFunc may take information from a request context and use it to\n// manipulate a Publisher. SubscriberResponseFuncs are only executed in\n// subscribers, after invoking the endpoint but prior to publishing a reply.\ntype SubscriberResponseFunc func(context.Context,\n\t*amqp.Delivery,\n\tChannel,\n\t*amqp.Publishing,\n) context.Context\n\n// PublisherResponseFunc may take information from an AMQP request and make the\n// response available for consumption. PublisherResponseFunc are only executed\n// in publishers, after a request has been made, but prior to it being decoded.\ntype PublisherResponseFunc func(context.Context, *amqp.Delivery) context.Context\n\n// SetPublishExchange returns a RequestFunc that sets the Exchange field\n// of an AMQP Publish call.\nfunc SetPublishExchange(publishExchange string) RequestFunc {\n\treturn func(ctx context.Context, pub *amqp.Publishing, _ *amqp.Delivery) context.Context {\n\t\treturn context.WithValue(ctx, ContextKeyExchange, publishExchange)\n\t}\n}\n\n// SetPublishKey returns a RequestFunc that sets the Key field\n// of an AMQP Publish call.\nfunc SetPublishKey(publishKey string) RequestFunc {\n\treturn func(ctx context.Context, pub *amqp.Publishing, _ *amqp.Delivery) context.Context {\n\t\treturn context.WithValue(ctx, ContextKeyPublishKey, publishKey)\n\t}\n}\n\n// SetPublishDeliveryMode sets the delivery mode of a Publishing.\n// Please refer to AMQP delivery mode constants in the AMQP package.\nfunc SetPublishDeliveryMode(dmode uint8) RequestFunc {\n\treturn func(ctx context.Context, pub *amqp.Publishing, _ *amqp.Delivery) context.Context {\n\t\tpub.DeliveryMode = dmode\n\t\treturn ctx\n\t}\n}\n\n// SetNackSleepDuration returns a RequestFunc that sets the amount of time\n// to sleep in the event of a Nack.\n// This has to be used in conjunction with an error encoder that Nack and sleeps.\n// One example is the SingleNackRequeueErrorEncoder.\n// It is designed to be used by Subscribers.\nfunc SetNackSleepDuration(duration time.Duration) RequestFunc {\n\treturn func(ctx context.Context, pub *amqp.Publishing, _ *amqp.Delivery) context.Context {\n\t\treturn context.WithValue(ctx, ContextKeyNackSleepDuration, duration)\n\t}\n}\n\n// SetConsumeAutoAck returns a RequestFunc that sets whether or not to autoAck\n// messages when consuming.\n// When set to false, the publisher will Ack the first message it receives with\n// a matching correlationId.\n// It is designed to be used by Publishers.\nfunc SetConsumeAutoAck(autoAck bool) RequestFunc {\n\treturn func(ctx context.Context, pub *amqp.Publishing, _ *amqp.Delivery) context.Context {\n\t\treturn context.WithValue(ctx, ContextKeyAutoAck, autoAck)\n\t}\n}\n\n// SetConsumeArgs returns a RequestFunc that set the arguments for amqp Consume\n// function.\n// It is designed to be used by Publishers.\nfunc SetConsumeArgs(args amqp.Table) RequestFunc {\n\treturn func(ctx context.Context, pub *amqp.Publishing, _ *amqp.Delivery) context.Context {\n\t\treturn context.WithValue(ctx, ContextKeyConsumeArgs, args)\n\t}\n}\n\n// SetContentType returns a RequestFunc that sets the ContentType field of\n// an AMQP Publishing.\nfunc SetContentType(contentType string) RequestFunc {\n\treturn func(ctx context.Context, pub *amqp.Publishing, _ *amqp.Delivery) context.Context {\n\t\tpub.ContentType = contentType\n\t\treturn ctx\n\t}\n}\n\n// SetContentEncoding returns a RequestFunc that sets the ContentEncoding field\n// of an AMQP Publishing.\nfunc SetContentEncoding(contentEncoding string) RequestFunc {\n\treturn func(ctx context.Context, pub *amqp.Publishing, _ *amqp.Delivery) context.Context {\n\t\tpub.ContentEncoding = contentEncoding\n\t\treturn ctx\n\t}\n}\n\n// SetCorrelationID returns a RequestFunc that sets the CorrelationId field\n// of an AMQP Publishing.\nfunc SetCorrelationID(cid string) RequestFunc {\n\treturn func(ctx context.Context, pub *amqp.Publishing, _ *amqp.Delivery) context.Context {\n\t\tpub.CorrelationId = cid\n\t\treturn ctx\n\t}\n}\n\n// SetAckAfterEndpoint returns a SubscriberResponseFunc that prompts the service\n// to Ack the Delivery object after successfully evaluating the endpoint,\n// and before it encodes the response.\n// It is designed to be used by Subscribers.\nfunc SetAckAfterEndpoint(multiple bool) SubscriberResponseFunc {\n\treturn func(ctx context.Context,\n\t\tdeliv *amqp.Delivery,\n\t\tch Channel,\n\t\tpub *amqp.Publishing,\n\t) context.Context {\n\t\tdeliv.Ack(multiple)\n\t\treturn ctx\n\t}\n}\n\nfunc getPublishExchange(ctx context.Context) string {\n\tif exchange := ctx.Value(ContextKeyExchange); exchange != nil {\n\t\treturn exchange.(string)\n\t}\n\treturn \"\"\n}\n\nfunc getPublishKey(ctx context.Context) string {\n\tif publishKey := ctx.Value(ContextKeyPublishKey); publishKey != nil {\n\t\treturn publishKey.(string)\n\t}\n\treturn \"\"\n}\n\nfunc getNackSleepDuration(ctx context.Context) time.Duration {\n\tif duration := ctx.Value(ContextKeyNackSleepDuration); duration != nil {\n\t\treturn duration.(time.Duration)\n\t}\n\treturn 0\n}\n\nfunc getConsumeAutoAck(ctx context.Context) bool {\n\tif autoAck := ctx.Value(ContextKeyAutoAck); autoAck != nil {\n\t\treturn autoAck.(bool)\n\t}\n\treturn false\n}\n\nfunc getConsumeArgs(ctx context.Context) amqp.Table {\n\tif args := ctx.Value(ContextKeyConsumeArgs); args != nil {\n\t\treturn args.(amqp.Table)\n\t}\n\treturn nil\n}\n\ntype contextKey int\n\nconst (\n\t// ContextKeyExchange is the value of the reply Exchange in\n\t// amqp.Publish.\n\tContextKeyExchange contextKey = iota\n\t// ContextKeyPublishKey is the value of the ReplyTo field in\n\t// amqp.Publish.\n\tContextKeyPublishKey\n\t// ContextKeyNackSleepDuration is the duration to sleep for if the\n\t// service Nack and requeues a message.\n\t// This is to prevent sporadic send-resending of message\n\t// when a message is constantly Nack'd and requeued.\n\tContextKeyNackSleepDuration\n\t// ContextKeyAutoAck is the value of autoAck field when calling\n\t// amqp.Channel.Consume.\n\tContextKeyAutoAck\n\t// ContextKeyConsumeArgs is the value of consumeArgs field when calling\n\t// amqp.Channel.Consume.\n\tContextKeyConsumeArgs\n)\n"
  },
  {
    "path": "transport/amqp/subscriber.go",
    "content": "package amqp\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/transport\"\n\t\"github.com/go-kit/log\"\n\tamqp \"github.com/rabbitmq/amqp091-go\"\n)\n\n// Subscriber wraps an endpoint and provides a handler for AMQP Delivery messages.\ntype Subscriber struct {\n\te                 endpoint.Endpoint\n\tdec               DecodeRequestFunc\n\tenc               EncodeResponseFunc\n\tbefore            []RequestFunc\n\tafter             []SubscriberResponseFunc\n\tresponsePublisher ResponsePublisher\n\terrorEncoder      ErrorEncoder\n\terrorHandler      transport.ErrorHandler\n}\n\n// NewSubscriber constructs a new subscriber, which provides a handler\n// for AMQP Delivery messages.\nfunc NewSubscriber(\n\te endpoint.Endpoint,\n\tdec DecodeRequestFunc,\n\tenc EncodeResponseFunc,\n\toptions ...SubscriberOption,\n) *Subscriber {\n\ts := &Subscriber{\n\t\te:                 e,\n\t\tdec:               dec,\n\t\tenc:               enc,\n\t\tresponsePublisher: DefaultResponsePublisher,\n\t\terrorEncoder:      DefaultErrorEncoder,\n\t\terrorHandler:      transport.NewLogErrorHandler(log.NewNopLogger()),\n\t}\n\tfor _, option := range options {\n\t\toption(s)\n\t}\n\treturn s\n}\n\n// SubscriberOption sets an optional parameter for subscribers.\ntype SubscriberOption func(*Subscriber)\n\n// SubscriberBefore functions are executed on the publisher delivery object\n// before the request is decoded.\nfunc SubscriberBefore(before ...RequestFunc) SubscriberOption {\n\treturn func(s *Subscriber) { s.before = append(s.before, before...) }\n}\n\n// SubscriberAfter functions are executed on the subscriber reply after the\n// endpoint is invoked, but before anything is published to the reply.\nfunc SubscriberAfter(after ...SubscriberResponseFunc) SubscriberOption {\n\treturn func(s *Subscriber) { s.after = append(s.after, after...) }\n}\n\n// SubscriberResponsePublisher is used by the subscriber to deliver response\n// objects to the original sender.\n// By default, the DefaultResponsePublisher is used.\nfunc SubscriberResponsePublisher(rp ResponsePublisher) SubscriberOption {\n\treturn func(s *Subscriber) { s.responsePublisher = rp }\n}\n\n// SubscriberErrorEncoder is used to encode errors to the subscriber reply\n// whenever they're encountered in the processing of a request. Clients can\n// use this to provide custom error formatting. By default,\n// errors will be published with the DefaultErrorEncoder.\nfunc SubscriberErrorEncoder(ee ErrorEncoder) SubscriberOption {\n\treturn func(s *Subscriber) { s.errorEncoder = ee }\n}\n\n// SubscriberErrorLogger is used to log non-terminal errors. By default, no errors\n// are logged. This is intended as a diagnostic measure. Finer-grained control\n// of error handling, including logging in more detail, should be performed in a\n// custom SubscriberErrorEncoder which has access to the context.\n// Deprecated: Use SubscriberErrorHandler instead.\nfunc SubscriberErrorLogger(logger log.Logger) SubscriberOption {\n\treturn func(s *Subscriber) { s.errorHandler = transport.NewLogErrorHandler(logger) }\n}\n\n// SubscriberErrorHandler is used to handle non-terminal errors. By default, non-terminal errors\n// are ignored. This is intended as a diagnostic measure. Finer-grained control\n// of error handling, including logging in more detail, should be performed in a\n// custom SubscriberErrorEncoder which has access to the context.\nfunc SubscriberErrorHandler(errorHandler transport.ErrorHandler) SubscriberOption {\n\treturn func(s *Subscriber) { s.errorHandler = errorHandler }\n}\n\n// ServeDelivery handles AMQP Delivery messages\n// It is strongly recommended to use *amqp.Channel as the\n// Channel interface implementation.\nfunc (s Subscriber) ServeDelivery(ch Channel) func(deliv *amqp.Delivery) {\n\treturn func(deliv *amqp.Delivery) {\n\t\tctx, cancel := context.WithCancel(context.Background())\n\t\tdefer cancel()\n\n\t\tpub := amqp.Publishing{}\n\n\t\tfor _, f := range s.before {\n\t\t\tctx = f(ctx, &pub, deliv)\n\t\t}\n\n\t\trequest, err := s.dec(ctx, deliv)\n\t\tif err != nil {\n\t\t\ts.errorHandler.Handle(ctx, err)\n\t\t\ts.errorEncoder(ctx, err, deliv, ch, &pub)\n\t\t\treturn\n\t\t}\n\n\t\tresponse, err := s.e(ctx, request)\n\t\tif err != nil {\n\t\t\ts.errorHandler.Handle(ctx, err)\n\t\t\ts.errorEncoder(ctx, err, deliv, ch, &pub)\n\t\t\treturn\n\t\t}\n\n\t\tfor _, f := range s.after {\n\t\t\tctx = f(ctx, deliv, ch, &pub)\n\t\t}\n\n\t\tif err := s.enc(ctx, &pub, response); err != nil {\n\t\t\ts.errorHandler.Handle(ctx, err)\n\t\t\ts.errorEncoder(ctx, err, deliv, ch, &pub)\n\t\t\treturn\n\t\t}\n\n\t\tif err := s.responsePublisher(ctx, deliv, ch, &pub); err != nil {\n\t\t\ts.errorHandler.Handle(ctx, err)\n\t\t\ts.errorEncoder(ctx, err, deliv, ch, &pub)\n\t\t\treturn\n\t\t}\n\t}\n\n}\n\n// EncodeJSONResponse marshals the response as JSON as part of the\n// payload of the AMQP Publishing object.\nfunc EncodeJSONResponse(\n\tctx context.Context,\n\tpub *amqp.Publishing,\n\tresponse interface{},\n) error {\n\tb, err := json.Marshal(response)\n\tif err != nil {\n\t\treturn err\n\t}\n\tpub.Body = b\n\treturn nil\n}\n\n// EncodeNopResponse is a response function that does nothing.\nfunc EncodeNopResponse(\n\tctx context.Context,\n\tpub *amqp.Publishing,\n\tresponse interface{},\n) error {\n\treturn nil\n}\n\n// ResponsePublisher functions are executed by the subscriber to\n// publish response object to the original sender.\n// Please note that the word \"publisher\" does not refer\n// to the publisher of pub/sub.\n// Rather, publisher is merely a function that publishes, or sends responses.\ntype ResponsePublisher func(\n\tcontext.Context,\n\t*amqp.Delivery,\n\tChannel,\n\t*amqp.Publishing,\n) error\n\n// DefaultResponsePublisher extracts the reply exchange and reply key\n// from the request, and sends the response object to that destination.\nfunc DefaultResponsePublisher(\n\tctx context.Context,\n\tdeliv *amqp.Delivery,\n\tch Channel,\n\tpub *amqp.Publishing,\n) error {\n\tif pub.CorrelationId == \"\" {\n\t\tpub.CorrelationId = deliv.CorrelationId\n\t}\n\n\treplyExchange := getPublishExchange(ctx)\n\treplyTo := getPublishKey(ctx)\n\tif replyTo == \"\" {\n\t\treplyTo = deliv.ReplyTo\n\t}\n\n\treturn ch.Publish(\n\t\treplyExchange,\n\t\treplyTo,\n\t\tfalse, // mandatory\n\t\tfalse, // immediate\n\t\t*pub,\n\t)\n}\n\n// NopResponsePublisher does not deliver a response to the original sender.\n// This response publisher is used when the user wants the subscriber to\n// receive and forget.\nfunc NopResponsePublisher(\n\tctx context.Context,\n\tdeliv *amqp.Delivery,\n\tch Channel,\n\tpub *amqp.Publishing,\n) error {\n\treturn nil\n}\n\n// ErrorEncoder is responsible for encoding an error to the subscriber reply.\n// Users are encouraged to use custom ErrorEncoders to encode errors to\n// their replies, and will likely want to pass and check for their own error\n// types.\ntype ErrorEncoder func(ctx context.Context,\n\terr error, deliv *amqp.Delivery, ch Channel, pub *amqp.Publishing)\n\n// DefaultErrorEncoder simply ignores the message. It does not reply\n// nor Ack/Nack the message.\nfunc DefaultErrorEncoder(ctx context.Context,\n\terr error, deliv *amqp.Delivery, ch Channel, pub *amqp.Publishing) {\n}\n\n// SingleNackRequeueErrorEncoder issues a Nack to the delivery with multiple flag set as false\n// and requeue flag set as true. It does not reply the message.\nfunc SingleNackRequeueErrorEncoder(ctx context.Context,\n\terr error, deliv *amqp.Delivery, ch Channel, pub *amqp.Publishing) {\n\tdeliv.Nack(\n\t\tfalse, //multiple\n\t\ttrue,  //requeue\n\t)\n\tduration := getNackSleepDuration(ctx)\n\ttime.Sleep(duration)\n}\n\n// ReplyErrorEncoder serializes the error message as a DefaultErrorResponse\n// JSON and sends the message to the ReplyTo address.\nfunc ReplyErrorEncoder(\n\tctx context.Context,\n\terr error,\n\tdeliv *amqp.Delivery,\n\tch Channel,\n\tpub *amqp.Publishing,\n) {\n\n\tif pub.CorrelationId == \"\" {\n\t\tpub.CorrelationId = deliv.CorrelationId\n\t}\n\n\treplyExchange := getPublishExchange(ctx)\n\treplyTo := getPublishKey(ctx)\n\tif replyTo == \"\" {\n\t\treplyTo = deliv.ReplyTo\n\t}\n\n\tresponse := DefaultErrorResponse{err.Error()}\n\n\tb, err := json.Marshal(response)\n\tif err != nil {\n\t\treturn\n\t}\n\tpub.Body = b\n\n\tch.Publish(\n\t\treplyExchange,\n\t\treplyTo,\n\t\tfalse, // mandatory\n\t\tfalse, // immediate\n\t\t*pub,\n\t)\n}\n\n// ReplyAndAckErrorEncoder serializes the error message as a DefaultErrorResponse\n// JSON and sends the message to the ReplyTo address then Acks the original\n// message.\nfunc ReplyAndAckErrorEncoder(ctx context.Context, err error, deliv *amqp.Delivery, ch Channel, pub *amqp.Publishing) {\n\tReplyErrorEncoder(ctx, err, deliv, ch, pub)\n\tdeliv.Ack(false)\n}\n\n// DefaultErrorResponse is the default structure of responses in the event\n// of an error.\ntype DefaultErrorResponse struct {\n\tError string `json:\"err\"`\n}\n\n// Channel is a channel interface to make testing possible.\n// It is highly recommended to use *amqp.Channel as the interface implementation.\ntype Channel interface {\n\tPublish(exchange, key string, mandatory, immediate bool, msg amqp.Publishing) error\n\tConsume(queue, consumer string, autoAck, exclusive, noLocal, noWail bool, args amqp.Table) (<-chan amqp.Delivery, error)\n}\n"
  },
  {
    "path": "transport/amqp/subscriber_test.go",
    "content": "package amqp_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\tamqptransport \"github.com/go-kit/kit/transport/amqp\"\n\tamqp \"github.com/rabbitmq/amqp091-go\"\n)\n\nvar (\n\terrTypeAssertion = errors.New(\"type assertion error\")\n)\n\n// mockChannel is a mock of *amqp.Channel.\ntype mockChannel struct {\n\tf          func(exchange, key string, mandatory, immediate bool)\n\tc          chan<- amqp.Publishing\n\tdeliveries []amqp.Delivery\n}\n\n// Publish runs a test function f and sends resultant message to a channel.\nfunc (ch *mockChannel) Publish(exchange, key string, mandatory, immediate bool, msg amqp.Publishing) error {\n\tch.f(exchange, key, mandatory, immediate)\n\tch.c <- msg\n\treturn nil\n}\n\nvar nullFunc = func(exchange, key string, mandatory, immediate bool) {\n}\n\nfunc (ch *mockChannel) Consume(queue, consumer string, autoAck, exclusive, noLocal, noWail bool, args amqp.Table) (<-chan amqp.Delivery, error) {\n\tc := make(chan amqp.Delivery, len(ch.deliveries))\n\tfor _, d := range ch.deliveries {\n\t\tc <- d\n\t}\n\treturn c, nil\n}\n\n// TestSubscriberBadDecode checks if decoder errors are handled properly.\nfunc TestSubscriberBadDecode(t *testing.T) {\n\tsub := amqptransport.NewSubscriber(\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil },\n\t\tfunc(context.Context, *amqp.Delivery) (interface{}, error) { return nil, errors.New(\"err!\") },\n\t\tfunc(context.Context, *amqp.Publishing, interface{}) error {\n\t\t\treturn nil\n\t\t},\n\t\tamqptransport.SubscriberErrorEncoder(amqptransport.ReplyErrorEncoder),\n\t)\n\n\toutputChan := make(chan amqp.Publishing, 1)\n\tch := &mockChannel{f: nullFunc, c: outputChan}\n\tsub.ServeDelivery(ch)(&amqp.Delivery{})\n\n\tvar msg amqp.Publishing\n\tselect {\n\tcase msg = <-outputChan:\n\t\tbreak\n\n\tcase <-time.After(100 * time.Millisecond):\n\t\tt.Fatal(\"Timed out waiting for publishing\")\n\t}\n\tres, err := decodeSubscriberError(msg)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif want, have := \"err!\", res.Error; want != have {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t}\n}\n\n// TestSubscriberBadEndpoint checks if endpoint errors are handled properly.\nfunc TestSubscriberBadEndpoint(t *testing.T) {\n\tsub := amqptransport.NewSubscriber(\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return nil, errors.New(\"err!\") },\n\t\tfunc(context.Context, *amqp.Delivery) (interface{}, error) { return struct{}{}, nil },\n\t\tfunc(context.Context, *amqp.Publishing, interface{}) error {\n\t\t\treturn nil\n\t\t},\n\t\tamqptransport.SubscriberErrorEncoder(amqptransport.ReplyErrorEncoder),\n\t)\n\n\toutputChan := make(chan amqp.Publishing, 1)\n\tch := &mockChannel{f: nullFunc, c: outputChan}\n\tsub.ServeDelivery(ch)(&amqp.Delivery{})\n\n\tvar msg amqp.Publishing\n\n\tselect {\n\tcase msg = <-outputChan:\n\t\tbreak\n\n\tcase <-time.After(100 * time.Millisecond):\n\t\tt.Fatal(\"Timed out waiting for publishing\")\n\t}\n\n\tres, err := decodeSubscriberError(msg)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif want, have := \"err!\", res.Error; want != have {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t}\n}\n\n// TestSubscriberBadEncoder checks if encoder errors are handled properly.\nfunc TestSubscriberBadEncoder(t *testing.T) {\n\tsub := amqptransport.NewSubscriber(\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil },\n\t\tfunc(context.Context, *amqp.Delivery) (interface{}, error) { return struct{}{}, nil },\n\t\tfunc(context.Context, *amqp.Publishing, interface{}) error {\n\t\t\treturn errors.New(\"err!\")\n\t\t},\n\t\tamqptransport.SubscriberErrorEncoder(amqptransport.ReplyErrorEncoder),\n\t)\n\n\toutputChan := make(chan amqp.Publishing, 1)\n\tch := &mockChannel{f: nullFunc, c: outputChan}\n\tsub.ServeDelivery(ch)(&amqp.Delivery{})\n\n\tvar msg amqp.Publishing\n\n\tselect {\n\tcase msg = <-outputChan:\n\t\tbreak\n\n\tcase <-time.After(100 * time.Millisecond):\n\t\tt.Fatal(\"Timed out waiting for publishing\")\n\t}\n\n\tres, err := decodeSubscriberError(msg)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif want, have := \"err!\", res.Error; want != have {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t}\n}\n\n// TestSubscriberSuccess checks if CorrelationId and ReplyTo are set properly\n// and if the payload is encoded properly.\nfunc TestSubscriberSuccess(t *testing.T) {\n\tcid := \"correlation\"\n\treplyTo := \"sender\"\n\tobj := testReq{\n\t\tSquadron: 436,\n\t}\n\tb, err := json.Marshal(obj)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tsub := amqptransport.NewSubscriber(\n\t\ttestEndpoint,\n\t\ttestReqDecoder,\n\t\tamqptransport.EncodeJSONResponse,\n\t\tamqptransport.SubscriberErrorEncoder(amqptransport.ReplyErrorEncoder),\n\t)\n\n\tcheckReplyToFunc := func(exchange, key string, mandatory, immediate bool) {\n\t\tif want, have := replyTo, key; want != have {\n\t\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t\t}\n\t}\n\n\toutputChan := make(chan amqp.Publishing, 1)\n\tch := &mockChannel{f: checkReplyToFunc, c: outputChan}\n\tsub.ServeDelivery(ch)(&amqp.Delivery{\n\t\tCorrelationId: cid,\n\t\tReplyTo:       replyTo,\n\t\tBody:          b,\n\t})\n\n\tvar msg amqp.Publishing\n\n\tselect {\n\tcase msg = <-outputChan:\n\t\tbreak\n\n\tcase <-time.After(100 * time.Millisecond):\n\t\tt.Fatal(\"Timed out waiting for publishing\")\n\t}\n\n\tif want, have := cid, msg.CorrelationId; want != have {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t}\n\n\t// check if error is not thrown\n\terrRes, err := decodeSubscriberError(msg)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif errRes.Error != \"\" {\n\t\tt.Error(\"Received error from subscriber\", errRes.Error)\n\t\treturn\n\t}\n\n\t// check obj vals\n\tresponse, err := testResDecoder(msg.Body)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tres, ok := response.(testRes)\n\tif !ok {\n\t\tt.Error(errTypeAssertion)\n\t}\n\n\tif want, have := obj.Squadron, res.Squadron; want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\tif want, have := names[obj.Squadron], res.Name; want != have {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t}\n}\n\n// TestNopResponseSubscriber checks if setting responsePublisher to\n// NopResponsePublisher works properly by disabling response.\nfunc TestNopResponseSubscriber(t *testing.T) {\n\tcid := \"correlation\"\n\treplyTo := \"sender\"\n\tobj := testReq{\n\t\tSquadron: 436,\n\t}\n\tb, err := json.Marshal(obj)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tsub := amqptransport.NewSubscriber(\n\t\ttestEndpoint,\n\t\ttestReqDecoder,\n\t\tamqptransport.EncodeJSONResponse,\n\t\tamqptransport.SubscriberResponsePublisher(amqptransport.NopResponsePublisher),\n\t\tamqptransport.SubscriberErrorEncoder(amqptransport.ReplyErrorEncoder),\n\t)\n\n\tcheckReplyToFunc := func(exchange, key string, mandatory, immediate bool) {}\n\n\toutputChan := make(chan amqp.Publishing, 1)\n\tch := &mockChannel{f: checkReplyToFunc, c: outputChan}\n\tsub.ServeDelivery(ch)(&amqp.Delivery{\n\t\tCorrelationId: cid,\n\t\tReplyTo:       replyTo,\n\t\tBody:          b,\n\t})\n\n\tselect {\n\tcase <-outputChan:\n\t\tt.Fatal(\"Subscriber with NopResponsePublisher replied.\")\n\tcase <-time.After(100 * time.Millisecond):\n\t\tbreak\n\t}\n}\n\n// TestSubscriberMultipleBefore checks if options to set exchange, key, deliveryMode\n// are working.\nfunc TestSubscriberMultipleBefore(t *testing.T) {\n\texchange := \"some exchange\"\n\tkey := \"some key\"\n\tdeliveryMode := uint8(127)\n\tcontentType := \"some content type\"\n\tcontentEncoding := \"some content encoding\"\n\tsub := amqptransport.NewSubscriber(\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil },\n\t\tfunc(context.Context, *amqp.Delivery) (interface{}, error) { return struct{}{}, nil },\n\t\tamqptransport.EncodeJSONResponse,\n\t\tamqptransport.SubscriberErrorEncoder(amqptransport.ReplyErrorEncoder),\n\t\tamqptransport.SubscriberBefore(\n\t\t\tamqptransport.SetPublishExchange(exchange),\n\t\t\tamqptransport.SetPublishKey(key),\n\t\t\tamqptransport.SetPublishDeliveryMode(deliveryMode),\n\t\t\tamqptransport.SetContentType(contentType),\n\t\t\tamqptransport.SetContentEncoding(contentEncoding),\n\t\t),\n\t)\n\tcheckReplyToFunc := func(exch, k string, mandatory, immediate bool) {\n\t\tif want, have := exchange, exch; want != have {\n\t\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t\t}\n\t\tif want, have := key, k; want != have {\n\t\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t\t}\n\t}\n\n\toutputChan := make(chan amqp.Publishing, 1)\n\tch := &mockChannel{f: checkReplyToFunc, c: outputChan}\n\tsub.ServeDelivery(ch)(&amqp.Delivery{})\n\n\tvar msg amqp.Publishing\n\n\tselect {\n\tcase msg = <-outputChan:\n\t\tbreak\n\n\tcase <-time.After(100 * time.Millisecond):\n\t\tt.Fatal(\"Timed out waiting for publishing\")\n\t}\n\n\t// check if error is not thrown\n\terrRes, err := decodeSubscriberError(msg)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif errRes.Error != \"\" {\n\t\tt.Error(\"Received error from subscriber\", errRes.Error)\n\t\treturn\n\t}\n\n\tif want, have := contentType, msg.ContentType; want != have {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t}\n\n\tif want, have := contentEncoding, msg.ContentEncoding; want != have {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t}\n\n\tif want, have := deliveryMode, msg.DeliveryMode; want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\n// TestDefaultContentMetaData checks that default ContentType and Content-Encoding\n// is not set as mentioned by AMQP specification.\nfunc TestDefaultContentMetaData(t *testing.T) {\n\tdefaultContentType := \"\"\n\tdefaultContentEncoding := \"\"\n\tsub := amqptransport.NewSubscriber(\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil },\n\t\tfunc(context.Context, *amqp.Delivery) (interface{}, error) { return struct{}{}, nil },\n\t\tamqptransport.EncodeJSONResponse,\n\t\tamqptransport.SubscriberErrorEncoder(amqptransport.ReplyErrorEncoder),\n\t)\n\tcheckReplyToFunc := func(exch, k string, mandatory, immediate bool) {}\n\toutputChan := make(chan amqp.Publishing, 1)\n\tch := &mockChannel{f: checkReplyToFunc, c: outputChan}\n\tsub.ServeDelivery(ch)(&amqp.Delivery{})\n\n\tvar msg amqp.Publishing\n\n\tselect {\n\tcase msg = <-outputChan:\n\t\tbreak\n\n\tcase <-time.After(100 * time.Millisecond):\n\t\tt.Fatal(\"Timed out waiting for publishing\")\n\t}\n\n\t// check if error is not thrown\n\terrRes, err := decodeSubscriberError(msg)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif errRes.Error != \"\" {\n\t\tt.Error(\"Received error from subscriber\", errRes.Error)\n\t\treturn\n\t}\n\n\tif want, have := defaultContentType, msg.ContentType; want != have {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t}\n\tif want, have := defaultContentEncoding, msg.ContentEncoding; want != have {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t}\n}\n\nfunc decodeSubscriberError(pub amqp.Publishing) (amqptransport.DefaultErrorResponse, error) {\n\tvar res amqptransport.DefaultErrorResponse\n\terr := json.Unmarshal(pub.Body, &res)\n\treturn res, err\n}\n\ntype testReq struct {\n\tSquadron int `json:\"s\"`\n}\ntype testRes struct {\n\tSquadron int    `json:\"s\"`\n\tName     string `json:\"n\"`\n}\n\nfunc testEndpoint(_ context.Context, request interface{}) (interface{}, error) {\n\treq, ok := request.(testReq)\n\tif !ok {\n\t\treturn nil, errTypeAssertion\n\t}\n\tname, prs := names[req.Squadron]\n\tif !prs {\n\t\treturn nil, errors.New(\"unknown squadron name\")\n\t}\n\tres := testRes{\n\t\tSquadron: req.Squadron,\n\t\tName:     name,\n\t}\n\treturn res, nil\n}\n\nfunc testReqDecoder(_ context.Context, d *amqp.Delivery) (interface{}, error) {\n\tvar obj testReq\n\terr := json.Unmarshal(d.Body, &obj)\n\treturn obj, err\n}\n\nfunc testReqEncoder(_ context.Context, p *amqp.Publishing, request interface{}) error {\n\treq, ok := request.(testReq)\n\tif !ok {\n\t\treturn errors.New(\"type assertion failure\")\n\t}\n\tb, err := json.Marshal(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tp.Body = b\n\treturn nil\n}\n\nfunc testResDeliveryDecoder(_ context.Context, d *amqp.Delivery) (interface{}, error) {\n\treturn testResDecoder(d.Body)\n}\n\nfunc testResDecoder(b []byte) (interface{}, error) {\n\tvar obj testRes\n\terr := json.Unmarshal(b, &obj)\n\treturn obj, err\n}\n\nvar names = map[int]string{\n\t424: \"tiger\",\n\t426: \"thunderbird\",\n\t429: \"bison\",\n\t436: \"tusker\",\n\t437: \"husky\",\n}\n"
  },
  {
    "path": "transport/amqp/util.go",
    "content": "package amqp\n\nimport (\n\t\"math/rand\"\n)\n\nfunc randomString(l int) string {\n\tbytes := make([]byte, l)\n\tfor i := 0; i < l; i++ {\n\t\tbytes[i] = byte(randInt(65, 90))\n\t}\n\treturn string(bytes)\n}\n\nfunc randInt(min int, max int) int {\n\treturn min + rand.Intn(max-min)\n}\n"
  },
  {
    "path": "transport/awslambda/doc.go",
    "content": "// Package awslambda provides an AWS Lambda transport layer.\npackage awslambda\n"
  },
  {
    "path": "transport/awslambda/encode_decode.go",
    "content": "package awslambda\n\nimport (\n\t\"context\"\n)\n\n// DecodeRequestFunc extracts a user-domain request object from an\n// AWS Lambda payload.\ntype DecodeRequestFunc func(context.Context, []byte) (interface{}, error)\n\n// EncodeResponseFunc encodes the passed response object into []byte,\n// ready to be sent as AWS Lambda response.\ntype EncodeResponseFunc func(context.Context, interface{}) ([]byte, error)\n\n// ErrorEncoder is responsible for encoding an error.\ntype ErrorEncoder func(ctx context.Context, err error) ([]byte, error)\n"
  },
  {
    "path": "transport/awslambda/handler.go",
    "content": "package awslambda\n\nimport (\n\t\"context\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/transport\"\n\t\"github.com/go-kit/log\"\n)\n\n// Handler wraps an endpoint.\ntype Handler struct {\n\te            endpoint.Endpoint\n\tdec          DecodeRequestFunc\n\tenc          EncodeResponseFunc\n\tbefore       []HandlerRequestFunc\n\tafter        []HandlerResponseFunc\n\terrorEncoder ErrorEncoder\n\tfinalizer    []HandlerFinalizerFunc\n\terrorHandler transport.ErrorHandler\n}\n\n// NewHandler constructs a new handler, which implements\n// the AWS lambda.Handler interface.\nfunc NewHandler(\n\te endpoint.Endpoint,\n\tdec DecodeRequestFunc,\n\tenc EncodeResponseFunc,\n\toptions ...HandlerOption,\n) *Handler {\n\th := &Handler{\n\t\te:            e,\n\t\tdec:          dec,\n\t\tenc:          enc,\n\t\terrorEncoder: DefaultErrorEncoder,\n\t\terrorHandler: transport.NewLogErrorHandler(log.NewNopLogger()),\n\t}\n\tfor _, option := range options {\n\t\toption(h)\n\t}\n\treturn h\n}\n\n// HandlerOption sets an optional parameter for handlers.\ntype HandlerOption func(*Handler)\n\n// HandlerBefore functions are executed on the payload byte,\n// before the request is decoded.\nfunc HandlerBefore(before ...HandlerRequestFunc) HandlerOption {\n\treturn func(h *Handler) { h.before = append(h.before, before...) }\n}\n\n// HandlerAfter functions are only executed after invoking the endpoint\n// but prior to returning a response.\nfunc HandlerAfter(after ...HandlerResponseFunc) HandlerOption {\n\treturn func(h *Handler) { h.after = append(h.after, after...) }\n}\n\n// HandlerErrorLogger is used to log non-terminal errors.\n// By default, no errors are logged.\n// Deprecated: Use HandlerErrorHandler instead.\nfunc HandlerErrorLogger(logger log.Logger) HandlerOption {\n\treturn func(h *Handler) { h.errorHandler = transport.NewLogErrorHandler(logger) }\n}\n\n// HandlerErrorHandler is used to handle non-terminal errors.\n// By default, non-terminal errors are ignored.\nfunc HandlerErrorHandler(errorHandler transport.ErrorHandler) HandlerOption {\n\treturn func(h *Handler) { h.errorHandler = errorHandler }\n}\n\n// HandlerErrorEncoder is used to encode errors.\nfunc HandlerErrorEncoder(ee ErrorEncoder) HandlerOption {\n\treturn func(h *Handler) { h.errorEncoder = ee }\n}\n\n// HandlerFinalizer sets finalizer which are called at the end of\n// request. By default no finalizer is registered.\nfunc HandlerFinalizer(f ...HandlerFinalizerFunc) HandlerOption {\n\treturn func(h *Handler) { h.finalizer = append(h.finalizer, f...) }\n}\n\n// DefaultErrorEncoder defines the default behavior of encoding an error response,\n// where it returns nil, and the error itself.\nfunc DefaultErrorEncoder(ctx context.Context, err error) ([]byte, error) {\n\treturn nil, err\n}\n\n// Invoke represents implementation of the AWS lambda.Handler interface.\nfunc (h *Handler) Invoke(\n\tctx context.Context,\n\tpayload []byte,\n) (resp []byte, err error) {\n\tif len(h.finalizer) > 0 {\n\t\tdefer func() {\n\t\t\tfor _, f := range h.finalizer {\n\t\t\t\tf(ctx, resp, err)\n\t\t\t}\n\t\t}()\n\t}\n\n\tfor _, f := range h.before {\n\t\tctx = f(ctx, payload)\n\t}\n\n\trequest, err := h.dec(ctx, payload)\n\tif err != nil {\n\t\th.errorHandler.Handle(ctx, err)\n\t\treturn h.errorEncoder(ctx, err)\n\t}\n\n\tresponse, err := h.e(ctx, request)\n\tif err != nil {\n\t\th.errorHandler.Handle(ctx, err)\n\t\treturn h.errorEncoder(ctx, err)\n\t}\n\n\tfor _, f := range h.after {\n\t\tctx = f(ctx, response)\n\t}\n\n\tif resp, err = h.enc(ctx, response); err != nil {\n\t\th.errorHandler.Handle(ctx, err)\n\t\treturn h.errorEncoder(ctx, err)\n\t}\n\n\treturn resp, err\n}\n"
  },
  {
    "path": "transport/awslambda/handler_test.go",
    "content": "package awslambda\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/transport\"\n\t\"github.com/go-kit/log\"\n)\n\ntype key int\n\nconst (\n\tKeyBeforeOne key = iota\n\tKeyBeforeTwo key = iota\n\tKeyAfterOne  key = iota\n\tKeyEncMode   key = iota\n)\n\n// Created based on github.com/aws/aws-lambda-go@v1.13.3/events.APIGatewayProxyRequest for the purposes of the tests below.\ntype apiGatewayProxyRequest struct {\n\tBody string `json:\"body\"`\n}\n\n// Created based on github.com/aws/aws-lambda-go@v1.13.3/events.APIGatewayProxyResponse for the purposes of the tests below.\ntype apiGatewayProxyResponse struct {\n\tStatusCode int    `json:\"statusCode\"`\n\tBody       string `json:\"body\"`\n}\n\nfunc TestDefaultErrorEncoder(t *testing.T) {\n\tctx := context.Background()\n\trootErr := fmt.Errorf(\"root\")\n\tb, err := DefaultErrorEncoder(ctx, rootErr)\n\tif b != nil {\n\t\tt.Fatalf(\"DefaultErrorEncoder should return nil as []byte\")\n\t}\n\tif err != rootErr {\n\t\tt.Fatalf(\"DefaultErrorEncoder expects return back the given error.\")\n\t}\n}\n\nfunc TestInvokeHappyPath(t *testing.T) {\n\tsvc := serviceTest01{}\n\n\thelloHandler := NewHandler(\n\t\tmakeTest01HelloEndpoint(svc),\n\t\tdecodeHelloRequestWithTwoBefores,\n\t\tencodeResponse,\n\t\tHandlerErrorHandler(transport.NewLogErrorHandler(log.NewNopLogger())),\n\t\tHandlerBefore(func(\n\t\t\tctx context.Context,\n\t\t\tpayload []byte,\n\t\t) context.Context {\n\t\t\tctx = context.WithValue(ctx, KeyBeforeOne, \"bef1\")\n\t\t\treturn ctx\n\t\t}),\n\t\tHandlerBefore(func(\n\t\t\tctx context.Context,\n\t\t\tpayload []byte,\n\t\t) context.Context {\n\t\t\tctx = context.WithValue(ctx, KeyBeforeTwo, \"bef2\")\n\t\t\treturn ctx\n\t\t}),\n\t\tHandlerAfter(func(\n\t\t\tctx context.Context,\n\t\t\tresponse interface{},\n\t\t) context.Context {\n\t\t\tctx = context.WithValue(ctx, KeyAfterOne, \"af1\")\n\t\t\treturn ctx\n\t\t}),\n\t\tHandlerAfter(func(\n\t\t\tctx context.Context,\n\t\t\tresponse interface{},\n\t\t) context.Context {\n\t\t\tif _, ok := ctx.Value(KeyAfterOne).(string); !ok {\n\t\t\t\tt.Fatalf(\"Value was not set properly during multi HandlerAfter\")\n\t\t\t}\n\t\t\treturn ctx\n\t\t}),\n\t\tHandlerFinalizer(func(\n\t\t\t_ context.Context,\n\t\t\tresp []byte,\n\t\t\t_ error,\n\t\t) {\n\t\t\tapigwResp := apiGatewayProxyResponse{}\n\t\t\terr := json.Unmarshal(resp, &apigwResp)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Should have no error, but got: %+v\", err)\n\t\t\t}\n\n\t\t\tresponse := helloResponse{}\n\t\t\terr = json.Unmarshal([]byte(apigwResp.Body), &response)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Should have no error, but got: %+v\", err)\n\t\t\t}\n\n\t\t\texpectedGreeting := \"hello john doe bef1 bef2\"\n\t\t\tif response.Greeting != expectedGreeting {\n\t\t\t\tt.Fatalf(\n\t\t\t\t\t\"Expect: %s, Actual: %s\", expectedGreeting, response.Greeting)\n\t\t\t}\n\t\t}),\n\t)\n\n\tctx := context.Background()\n\treq, _ := json.Marshal(apiGatewayProxyRequest{\n\t\tBody: `{\"name\":\"john doe\"}`,\n\t})\n\tresp, err := helloHandler.Invoke(ctx, req)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Should have no error, but got: %+v\", err)\n\t}\n\n\tapigwResp := apiGatewayProxyResponse{}\n\terr = json.Unmarshal(resp, &apigwResp)\n\tif err != nil {\n\t\tt.Fatalf(\"Should have no error, but got: %+v\", err)\n\t}\n\n\tresponse := helloResponse{}\n\terr = json.Unmarshal([]byte(apigwResp.Body), &response)\n\tif err != nil {\n\t\tt.Fatalf(\"Should have no error, but got: %+v\", err)\n\t}\n\n\texpectedGreeting := \"hello john doe bef1 bef2\"\n\tif response.Greeting != expectedGreeting {\n\t\tt.Fatalf(\n\t\t\t\"Expect: %s, Actual: %s\", expectedGreeting, response.Greeting)\n\t}\n}\n\nfunc TestInvokeFailDecode(t *testing.T) {\n\tsvc := serviceTest01{}\n\n\thelloHandler := NewHandler(\n\t\tmakeTest01HelloEndpoint(svc),\n\t\tdecodeHelloRequestWithTwoBefores,\n\t\tencodeResponse,\n\t\tHandlerErrorEncoder(func(\n\t\t\tctx context.Context,\n\t\t\terr error,\n\t\t) ([]byte, error) {\n\t\t\tapigwResp := apiGatewayProxyResponse{}\n\t\t\tapigwResp.Body = `{\"error\":\"yes\"}`\n\t\t\tapigwResp.StatusCode = 500\n\t\t\tresp, err := json.Marshal(apigwResp)\n\t\t\treturn resp, err\n\t\t}),\n\t)\n\n\tctx := context.Background()\n\treq, _ := json.Marshal(apiGatewayProxyRequest{\n\t\tBody: `{\"name\":\"john doe\"}`,\n\t})\n\tresp, err := helloHandler.Invoke(ctx, req)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Should have no error, but got: %+v\", err)\n\t}\n\n\tapigwResp := apiGatewayProxyResponse{}\n\tjson.Unmarshal(resp, &apigwResp)\n\tif apigwResp.StatusCode != 500 {\n\t\tt.Fatalf(\"Expect status code of 500, instead of %d\", apigwResp.StatusCode)\n\t}\n}\n\nfunc TestInvokeFailEndpoint(t *testing.T) {\n\tsvc := serviceTest01{}\n\n\thelloHandler := NewHandler(\n\t\tmakeTest01FailEndpoint(svc),\n\t\tdecodeHelloRequestWithTwoBefores,\n\t\tencodeResponse,\n\t\tHandlerBefore(func(\n\t\t\tctx context.Context,\n\t\t\tpayload []byte,\n\t\t) context.Context {\n\t\t\tctx = context.WithValue(ctx, KeyBeforeOne, \"bef1\")\n\t\t\treturn ctx\n\t\t}),\n\t\tHandlerBefore(func(\n\t\t\tctx context.Context,\n\t\t\tpayload []byte,\n\t\t) context.Context {\n\t\t\tctx = context.WithValue(ctx, KeyBeforeTwo, \"bef2\")\n\t\t\treturn ctx\n\t\t}),\n\t\tHandlerErrorEncoder(func(\n\t\t\tctx context.Context,\n\t\t\terr error,\n\t\t) ([]byte, error) {\n\t\t\tapigwResp := apiGatewayProxyResponse{}\n\t\t\tapigwResp.Body = `{\"error\":\"yes\"}`\n\t\t\tapigwResp.StatusCode = 500\n\t\t\tresp, err := json.Marshal(apigwResp)\n\t\t\treturn resp, err\n\t\t}),\n\t)\n\n\tctx := context.Background()\n\treq, _ := json.Marshal(apiGatewayProxyRequest{\n\t\tBody: `{\"name\":\"john doe\"}`,\n\t})\n\tresp, err := helloHandler.Invoke(ctx, req)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Should have no error, but got: %+v\", err)\n\t}\n\n\tapigwResp := apiGatewayProxyResponse{}\n\tjson.Unmarshal(resp, &apigwResp)\n\tif apigwResp.StatusCode != 500 {\n\t\tt.Fatalf(\"Expect status code of 500, instead of %d\", apigwResp.StatusCode)\n\t}\n}\n\nfunc TestInvokeFailEncode(t *testing.T) {\n\tsvc := serviceTest01{}\n\n\thelloHandler := NewHandler(\n\t\tmakeTest01HelloEndpoint(svc),\n\t\tdecodeHelloRequestWithTwoBefores,\n\t\tencodeResponse,\n\t\tHandlerBefore(func(\n\t\t\tctx context.Context,\n\t\t\tpayload []byte,\n\t\t) context.Context {\n\t\t\tctx = context.WithValue(ctx, KeyBeforeOne, \"bef1\")\n\t\t\treturn ctx\n\t\t}),\n\t\tHandlerBefore(func(\n\t\t\tctx context.Context,\n\t\t\tpayload []byte,\n\t\t) context.Context {\n\t\t\tctx = context.WithValue(ctx, KeyBeforeTwo, \"bef2\")\n\t\t\treturn ctx\n\t\t}),\n\t\tHandlerAfter(func(\n\t\t\tctx context.Context,\n\t\t\tresponse interface{},\n\t\t) context.Context {\n\t\t\tctx = context.WithValue(ctx, KeyEncMode, \"fail_encode\")\n\t\t\treturn ctx\n\t\t}),\n\t\tHandlerErrorEncoder(func(\n\t\t\tctx context.Context,\n\t\t\terr error,\n\t\t) ([]byte, error) {\n\t\t\t// convert error into proper APIGateway response.\n\t\t\tapigwResp := apiGatewayProxyResponse{}\n\t\t\tapigwResp.Body = `{\"error\":\"yes\"}`\n\t\t\tapigwResp.StatusCode = 500\n\t\t\tresp, err := json.Marshal(apigwResp)\n\t\t\treturn resp, err\n\t\t}),\n\t)\n\n\tctx := context.Background()\n\treq, _ := json.Marshal(apiGatewayProxyRequest{\n\t\tBody: `{\"name\":\"john doe\"}`,\n\t})\n\tresp, err := helloHandler.Invoke(ctx, req)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Should have no error, but got: %+v\", err)\n\t}\n\n\tapigwResp := apiGatewayProxyResponse{}\n\tjson.Unmarshal(resp, &apigwResp)\n\tif apigwResp.StatusCode != 500 {\n\t\tt.Fatalf(\"Expect status code of 500, instead of %d\", apigwResp.StatusCode)\n\t}\n}\n\nfunc decodeHelloRequestWithTwoBefores(\n\tctx context.Context, req []byte,\n) (interface{}, error) {\n\tapigwReq := apiGatewayProxyRequest{}\n\terr := json.Unmarshal([]byte(req), &apigwReq)\n\tif err != nil {\n\t\treturn apigwReq, err\n\t}\n\n\trequest := helloRequest{}\n\terr = json.Unmarshal([]byte(apigwReq.Body), &request)\n\tif err != nil {\n\t\treturn request, err\n\t}\n\n\tvalOne, ok := ctx.Value(KeyBeforeOne).(string)\n\tif !ok {\n\t\treturn request, fmt.Errorf(\n\t\t\t\"Value was not set properly when multiple HandlerBefores are used\")\n\t}\n\n\tvalTwo, ok := ctx.Value(KeyBeforeTwo).(string)\n\tif !ok {\n\t\treturn request, fmt.Errorf(\n\t\t\t\"Value was not set properly when multiple HandlerBefores are used\")\n\t}\n\n\trequest.Name += \" \" + valOne + \" \" + valTwo\n\treturn request, err\n}\n\nfunc encodeResponse(\n\tctx context.Context, response interface{},\n) ([]byte, error) {\n\tapigwResp := apiGatewayProxyResponse{}\n\n\tmode, ok := ctx.Value(KeyEncMode).(string)\n\tif ok && mode == \"fail_encode\" {\n\t\treturn nil, fmt.Errorf(\"fail encoding\")\n\t}\n\n\trespByte, err := json.Marshal(response)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tapigwResp.Body = string(respByte)\n\tapigwResp.StatusCode = 200\n\n\tresp, err := json.Marshal(apigwResp)\n\treturn resp, err\n}\n\ntype helloRequest struct {\n\tName string `json:\"name\"`\n}\n\ntype helloResponse struct {\n\tGreeting string `json:\"greeting\"`\n}\n\nfunc makeTest01HelloEndpoint(svc serviceTest01) endpoint.Endpoint {\n\treturn func(_ context.Context, request interface{}) (interface{}, error) {\n\t\treq := request.(helloRequest)\n\t\tgreeting := svc.hello(req.Name)\n\t\treturn helloResponse{greeting}, nil\n\t}\n}\n\nfunc makeTest01FailEndpoint(_ serviceTest01) endpoint.Endpoint {\n\treturn func(_ context.Context, request interface{}) (interface{}, error) {\n\t\treturn nil, fmt.Errorf(\"test error endpoint\")\n\t}\n}\n\ntype serviceTest01 struct{}\n\nfunc (ts *serviceTest01) hello(name string) string {\n\treturn fmt.Sprintf(\"hello %s\", name)\n}\n"
  },
  {
    "path": "transport/awslambda/request_response_funcs.go",
    "content": "package awslambda\n\nimport (\n\t\"context\"\n)\n\n// HandlerRequestFunc may take information from the received\n// payload and use it to place items in the request scoped context.\n// HandlerRequestFuncs are executed prior to invoking the endpoint and\n// decoding of the payload.\ntype HandlerRequestFunc func(ctx context.Context, payload []byte) context.Context\n\n// HandlerResponseFunc may take information from a request context\n// and use it to manipulate the response before it's marshaled.\n// HandlerResponseFunc are executed after invoking the endpoint\n// but prior to returning a response.\ntype HandlerResponseFunc func(ctx context.Context, response interface{}) context.Context\n\n// HandlerFinalizerFunc is executed at the end of Invoke.\n// This can be used for logging purposes.\ntype HandlerFinalizerFunc func(ctx context.Context, resp []byte, err error)\n"
  },
  {
    "path": "transport/doc.go",
    "content": "// Package transport contains helpers applicable to all supported transports.\npackage transport\n"
  },
  {
    "path": "transport/error_handler.go",
    "content": "package transport\n\nimport (\n\t\"context\"\n\n\t\"github.com/go-kit/log\"\n)\n\n// ErrorHandler receives a transport error to be processed for diagnostic purposes.\n// Usually this means logging the error.\ntype ErrorHandler interface {\n\tHandle(ctx context.Context, err error)\n}\n\n// LogErrorHandler is a transport error handler implementation which logs an error.\ntype LogErrorHandler struct {\n\tlogger log.Logger\n}\n\nfunc NewLogErrorHandler(logger log.Logger) *LogErrorHandler {\n\treturn &LogErrorHandler{\n\t\tlogger: logger,\n\t}\n}\n\nfunc (h *LogErrorHandler) Handle(ctx context.Context, err error) {\n\th.logger.Log(\"err\", err)\n}\n\n// The ErrorHandlerFunc type is an adapter to allow the use of\n// ordinary function as ErrorHandler. If f is a function\n// with the appropriate signature, ErrorHandlerFunc(f) is a\n// ErrorHandler that calls f.\ntype ErrorHandlerFunc func(ctx context.Context, err error)\n\n// Handle calls f(ctx, err).\nfunc (f ErrorHandlerFunc) Handle(ctx context.Context, err error) {\n\tf(ctx, err)\n}\n"
  },
  {
    "path": "transport/error_handler_test.go",
    "content": "package transport_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/go-kit/kit/transport\"\n\t\"github.com/go-kit/log\"\n)\n\nfunc TestLogErrorHandler(t *testing.T) {\n\tvar output []interface{}\n\n\tlogger := log.Logger(log.LoggerFunc(func(keyvals ...interface{}) error {\n\t\toutput = append(output, keyvals...)\n\t\treturn nil\n\t}))\n\n\terrorHandler := transport.NewLogErrorHandler(logger)\n\n\terr := errors.New(\"error\")\n\n\terrorHandler.Handle(context.Background(), err)\n\n\tif output[1] != err {\n\t\tt.Errorf(\"expected an error log event: have %v, want %v\", output[1], err)\n\t}\n}\n"
  },
  {
    "path": "transport/grpc/README.md",
    "content": "# grpc\n\n[gRPC](http://www.grpc.io/) is an excellent, modern IDL and transport for\nmicroservices. If you're starting a greenfield project, go-kit strongly\nrecommends gRPC as your default transport.\n\nOne important note is that while gRPC supports streaming requests and replies,\ngo-kit does not. You can still use streams in your service, but their\nimplementation will not be able to take advantage of many go-kit features like middleware.\n\nUsing gRPC and go-kit together is very simple.\n\nFirst, define your service using protobuf3. This is explained\n[in gRPC documentation](http://www.grpc.io/docs/#defining-a-service).\nSee\n[addsvc.proto](https://github.com/go-kit/examples/blob/master/addsvc/pb/addsvc.proto)\nfor an example. Make sure the proto definition matches your service's go-kit\n(interface) definition.\n\nNext, get the protoc compiler.\n\nYou can download pre-compiled binaries from the\n[protobuf release page](https://github.com/google/protobuf/releases).\nYou will unzip a folder called `protoc3` with a subdirectory `bin` containing\nan executable. Move that executable somewhere in your `$PATH` and you're good\nto go!\n\nIt can also be built from source.\n\n```sh\nbrew install autoconf automake libtool\ngit clone https://github.com/google/protobuf\ncd protobuf\n./autogen.sh ; ./configure ; make ; make install\n```\n\nThen, compile your service definition, from .proto to .go.\n\n```sh\nprotoc add.proto --go_out=plugins=grpc:.\n```\n\nFinally, write a tiny binding from your service definition to the gRPC\ndefinition. It's a simple conversion from one domain to another.\nSee\n[grpc.go](https://github.com/go-kit/examples/blob/master/addsvc/pkg/addtransport/grpc.go)\nfor an example.\n\nThat's it!\nThe gRPC binding can be bound to a listener and serve normal gRPC requests.\nAnd within your service, you can use standard go-kit components and idioms.\nSee [addsvc](https://github.com/go-kit/examples/tree/master/addsvc/) for\na complete working example with gRPC support. And remember: go-kit services\ncan support multiple transports simultaneously.\n"
  },
  {
    "path": "transport/grpc/_grpc_test/client.go",
    "content": "package test\n\nimport (\n\t\"context\"\n\n\t\"google.golang.org/grpc\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\tgrpctransport \"github.com/go-kit/kit/transport/grpc\"\n\t\"github.com/go-kit/kit/transport/grpc/_grpc_test/pb\"\n)\n\ntype clientBinding struct {\n\ttest endpoint.Endpoint\n}\n\nfunc (c *clientBinding) Test(ctx context.Context, a string, b int64) (context.Context, string, error) {\n\tresponse, err := c.test(ctx, TestRequest{A: a, B: b})\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tr := response.(*TestResponse)\n\treturn r.Ctx, r.V, nil\n}\n\nfunc NewClient(cc *grpc.ClientConn) Service {\n\treturn &clientBinding{\n\t\ttest: grpctransport.NewClient(\n\t\t\tcc,\n\t\t\t\"pb.Test\",\n\t\t\t\"Test\",\n\t\t\tencodeRequest,\n\t\t\tdecodeResponse,\n\t\t\t&pb.TestResponse{},\n\t\t\tgrpctransport.ClientBefore(\n\t\t\t\tinjectCorrelationID,\n\t\t\t),\n\t\t\tgrpctransport.ClientBefore(\n\t\t\t\tdisplayClientRequestHeaders,\n\t\t\t),\n\t\t\tgrpctransport.ClientAfter(\n\t\t\t\tdisplayClientResponseHeaders,\n\t\t\t\tdisplayClientResponseTrailers,\n\t\t\t),\n\t\t\tgrpctransport.ClientAfter(\n\t\t\t\textractConsumedCorrelationID,\n\t\t\t),\n\t\t).Endpoint(),\n\t}\n}\n"
  },
  {
    "path": "transport/grpc/_grpc_test/context_metadata.go",
    "content": "package test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"google.golang.org/grpc/metadata\"\n)\n\ntype metaContext string\n\nconst (\n\tcorrelationID     metaContext = \"correlation-id\"\n\tresponseHDR       metaContext = \"my-response-header\"\n\tresponseTRLR      metaContext = \"my-response-trailer\"\n\tcorrelationIDTRLR metaContext = \"correlation-id-consumed\"\n)\n\n/* client before functions */\n\nfunc injectCorrelationID(ctx context.Context, md *metadata.MD) context.Context {\n\tif hdr, ok := ctx.Value(correlationID).(string); ok {\n\t\tfmt.Printf(\"\\tClient found correlationID %q in context, set metadata header\\n\", hdr)\n\t\t(*md)[string(correlationID)] = append((*md)[string(correlationID)], hdr)\n\t}\n\treturn ctx\n}\n\nfunc displayClientRequestHeaders(ctx context.Context, md *metadata.MD) context.Context {\n\tif len(*md) > 0 {\n\t\tfmt.Println(\"\\tClient >> Request Headers:\")\n\t\tfor key, val := range *md {\n\t\t\tfmt.Printf(\"\\t\\t%s: %s\\n\", key, val[len(val)-1])\n\t\t}\n\t}\n\treturn ctx\n}\n\n/* server before functions */\n\nfunc extractCorrelationID(ctx context.Context, md metadata.MD) context.Context {\n\tif hdr, ok := md[string(correlationID)]; ok {\n\t\tcID := hdr[len(hdr)-1]\n\t\tctx = context.WithValue(ctx, correlationID, cID)\n\t\tfmt.Printf(\"\\tServer received correlationID %q in metadata header, set context\\n\", cID)\n\t}\n\treturn ctx\n}\n\nfunc displayServerRequestHeaders(ctx context.Context, md metadata.MD) context.Context {\n\tif len(md) > 0 {\n\t\tfmt.Println(\"\\tServer << Request Headers:\")\n\t\tfor key, val := range md {\n\t\t\tfmt.Printf(\"\\t\\t%s: %s\\n\", key, val[len(val)-1])\n\t\t}\n\t}\n\treturn ctx\n}\n\n/* server after functions */\n\nfunc injectResponseHeader(ctx context.Context, md *metadata.MD, _ *metadata.MD) context.Context {\n\t*md = metadata.Join(*md, metadata.Pairs(string(responseHDR), \"has-a-value\"))\n\treturn ctx\n}\n\nfunc displayServerResponseHeaders(ctx context.Context, md *metadata.MD, _ *metadata.MD) context.Context {\n\tif len(*md) > 0 {\n\t\tfmt.Println(\"\\tServer >> Response Headers:\")\n\t\tfor key, val := range *md {\n\t\t\tfmt.Printf(\"\\t\\t%s: %s\\n\", key, val[len(val)-1])\n\t\t}\n\t}\n\treturn ctx\n}\n\nfunc injectResponseTrailer(ctx context.Context, _ *metadata.MD, md *metadata.MD) context.Context {\n\t*md = metadata.Join(*md, metadata.Pairs(string(responseTRLR), \"has-a-value-too\"))\n\treturn ctx\n}\n\nfunc injectConsumedCorrelationID(ctx context.Context, _ *metadata.MD, md *metadata.MD) context.Context {\n\tif hdr, ok := ctx.Value(correlationID).(string); ok {\n\t\tfmt.Printf(\"\\tServer found correlationID %q in context, set consumed trailer\\n\", hdr)\n\t\t*md = metadata.Join(*md, metadata.Pairs(string(correlationIDTRLR), hdr))\n\t}\n\treturn ctx\n}\n\nfunc displayServerResponseTrailers(ctx context.Context, _ *metadata.MD, md *metadata.MD) context.Context {\n\tif len(*md) > 0 {\n\t\tfmt.Println(\"\\tServer >> Response Trailers:\")\n\t\tfor key, val := range *md {\n\t\t\tfmt.Printf(\"\\t\\t%s: %s\\n\", key, val[len(val)-1])\n\t\t}\n\t}\n\treturn ctx\n}\n\n/* client after functions */\n\nfunc displayClientResponseHeaders(ctx context.Context, md metadata.MD, _ metadata.MD) context.Context {\n\tif len(md) > 0 {\n\t\tfmt.Println(\"\\tClient << Response Headers:\")\n\t\tfor key, val := range md {\n\t\t\tfmt.Printf(\"\\t\\t%s: %s\\n\", key, val[len(val)-1])\n\t\t}\n\t}\n\treturn ctx\n}\n\nfunc displayClientResponseTrailers(ctx context.Context, _ metadata.MD, md metadata.MD) context.Context {\n\tif len(md) > 0 {\n\t\tfmt.Println(\"\\tClient << Response Trailers:\")\n\t\tfor key, val := range md {\n\t\t\tfmt.Printf(\"\\t\\t%s: %s\\n\", key, val[len(val)-1])\n\t\t}\n\t}\n\treturn ctx\n}\n\nfunc extractConsumedCorrelationID(ctx context.Context, _ metadata.MD, md metadata.MD) context.Context {\n\tif hdr, ok := md[string(correlationIDTRLR)]; ok {\n\t\tfmt.Printf(\"\\tClient received consumed correlationID %q in metadata trailer, set context\\n\", hdr[len(hdr)-1])\n\t\tctx = context.WithValue(ctx, correlationIDTRLR, hdr[len(hdr)-1])\n\t}\n\treturn ctx\n}\n\n/* CorrelationID context handlers */\n\nfunc SetCorrelationID(ctx context.Context, v string) context.Context {\n\treturn context.WithValue(ctx, correlationID, v)\n}\n\nfunc GetConsumedCorrelationID(ctx context.Context) string {\n\tif trlr, ok := ctx.Value(correlationIDTRLR).(string); ok {\n\t\treturn trlr\n\t}\n\treturn \"\"\n}\n"
  },
  {
    "path": "transport/grpc/_grpc_test/pb/generate.go",
    "content": "package pb\n\n//go:generate protoc test.proto --go_out=. --go-grpc_out=. --go_opt=Mtest.proto=github.com/go-kit/kit/transport/grpc/_grpc_test/pb --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go-grpc_opt=Mtest.proto=github.com/go-kit/kit/transport/grpc/_grpc_test/pb\n"
  },
  {
    "path": "transport/grpc/_grpc_test/pb/test.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.26.0\n// \tprotoc        v3.16.0\n// source: test.proto\n\npackage pb\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype TestRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tA string `protobuf:\"bytes,1,opt,name=a,proto3\" json:\"a,omitempty\"`\n\tB int64  `protobuf:\"varint,2,opt,name=b,proto3\" json:\"b,omitempty\"`\n}\n\nfunc (x *TestRequest) Reset() {\n\t*x = TestRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_test_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *TestRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*TestRequest) ProtoMessage() {}\n\nfunc (x *TestRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_test_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use TestRequest.ProtoReflect.Descriptor instead.\nfunc (*TestRequest) Descriptor() ([]byte, []int) {\n\treturn file_test_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *TestRequest) GetA() string {\n\tif x != nil {\n\t\treturn x.A\n\t}\n\treturn \"\"\n}\n\nfunc (x *TestRequest) GetB() int64 {\n\tif x != nil {\n\t\treturn x.B\n\t}\n\treturn 0\n}\n\ntype TestResponse struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tV string `protobuf:\"bytes,1,opt,name=v,proto3\" json:\"v,omitempty\"`\n}\n\nfunc (x *TestResponse) Reset() {\n\t*x = TestResponse{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_test_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *TestResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*TestResponse) ProtoMessage() {}\n\nfunc (x *TestResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_test_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use TestResponse.ProtoReflect.Descriptor instead.\nfunc (*TestResponse) Descriptor() ([]byte, []int) {\n\treturn file_test_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *TestResponse) GetV() string {\n\tif x != nil {\n\t\treturn x.V\n\t}\n\treturn \"\"\n}\n\nvar File_test_proto protoreflect.FileDescriptor\n\nvar file_test_proto_rawDesc = []byte{\n\t0x0a, 0x0a, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x70, 0x62,\n\t0x22, 0x29, 0x0a, 0x0b, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,\n\t0x0c, 0x0a, 0x01, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x01, 0x61, 0x12, 0x0c, 0x0a,\n\t0x01, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x01, 0x62, 0x22, 0x1c, 0x0a, 0x0c, 0x54,\n\t0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0c, 0x0a, 0x01, 0x76,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x01, 0x76, 0x32, 0x33, 0x0a, 0x04, 0x54, 0x65, 0x73,\n\t0x74, 0x12, 0x2b, 0x0a, 0x04, 0x54, 0x65, 0x73, 0x74, 0x12, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x54,\n\t0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x70, 0x62, 0x2e,\n\t0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x62, 0x06,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_test_proto_rawDescOnce sync.Once\n\tfile_test_proto_rawDescData = file_test_proto_rawDesc\n)\n\nfunc file_test_proto_rawDescGZIP() []byte {\n\tfile_test_proto_rawDescOnce.Do(func() {\n\t\tfile_test_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_proto_rawDescData)\n\t})\n\treturn file_test_proto_rawDescData\n}\n\nvar file_test_proto_msgTypes = make([]protoimpl.MessageInfo, 2)\nvar file_test_proto_goTypes = []interface{}{\n\t(*TestRequest)(nil),  // 0: pb.TestRequest\n\t(*TestResponse)(nil), // 1: pb.TestResponse\n}\nvar file_test_proto_depIdxs = []int32{\n\t0, // 0: pb.Test.Test:input_type -> pb.TestRequest\n\t1, // 1: pb.Test.Test:output_type -> pb.TestResponse\n\t1, // [1:2] is the sub-list for method output_type\n\t0, // [0:1] is the sub-list for method input_type\n\t0, // [0:0] is the sub-list for extension type_name\n\t0, // [0:0] is the sub-list for extension extendee\n\t0, // [0:0] is the sub-list for field type_name\n}\n\nfunc init() { file_test_proto_init() }\nfunc file_test_proto_init() {\n\tif File_test_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_test_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*TestRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_test_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*TestResponse); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_test_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   2,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_test_proto_goTypes,\n\t\tDependencyIndexes: file_test_proto_depIdxs,\n\t\tMessageInfos:      file_test_proto_msgTypes,\n\t}.Build()\n\tFile_test_proto = out.File\n\tfile_test_proto_rawDesc = nil\n\tfile_test_proto_goTypes = nil\n\tfile_test_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "transport/grpc/_grpc_test/pb/test.proto",
    "content": "syntax = \"proto3\";\n\npackage pb;\n\nservice Test {\n  rpc Test (TestRequest) returns (TestResponse) {}\n}\n\nmessage TestRequest {\n  string a = 1;\n  int64 b = 2;\n}\n\nmessage TestResponse {\n  string v = 1;\n}\n"
  },
  {
    "path": "transport/grpc/_grpc_test/pb/test_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n\npackage pb\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang.org/grpc\"\n\tcodes \"google.golang.org/grpc/codes\"\n\tstatus \"google.golang.org/grpc/status\"\n)\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the grpc package it is being compiled against.\n// Requires gRPC-Go v1.32.0 or later.\nconst _ = grpc.SupportPackageIsVersion7\n\n// TestClient is the client API for Test service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\ntype TestClient interface {\n\tTest(ctx context.Context, in *TestRequest, opts ...grpc.CallOption) (*TestResponse, error)\n}\n\ntype testClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewTestClient(cc grpc.ClientConnInterface) TestClient {\n\treturn &testClient{cc}\n}\n\nfunc (c *testClient) Test(ctx context.Context, in *TestRequest, opts ...grpc.CallOption) (*TestResponse, error) {\n\tout := new(TestResponse)\n\terr := c.cc.Invoke(ctx, \"/pb.Test/Test\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// TestServer is the server API for Test service.\n// All implementations must embed UnimplementedTestServer\n// for forward compatibility\ntype TestServer interface {\n\tTest(context.Context, *TestRequest) (*TestResponse, error)\n\tmustEmbedUnimplementedTestServer()\n}\n\n// UnimplementedTestServer must be embedded to have forward compatible implementations.\ntype UnimplementedTestServer struct {\n}\n\nfunc (UnimplementedTestServer) Test(context.Context, *TestRequest) (*TestResponse, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method Test not implemented\")\n}\nfunc (UnimplementedTestServer) mustEmbedUnimplementedTestServer() {}\n\n// UnsafeTestServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to TestServer will\n// result in compilation errors.\ntype UnsafeTestServer interface {\n\tmustEmbedUnimplementedTestServer()\n}\n\nfunc RegisterTestServer(s grpc.ServiceRegistrar, srv TestServer) {\n\ts.RegisterService(&Test_ServiceDesc, srv)\n}\n\nfunc _Test_Test_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(TestRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(TestServer).Test(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/pb.Test/Test\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(TestServer).Test(ctx, req.(*TestRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// Test_ServiceDesc is the grpc.ServiceDesc for Test service.\n// It's only intended for direct use with grpc.RegisterService,\n// and not to be introspected or modified (even as a copy)\nvar Test_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"pb.Test\",\n\tHandlerType: (*TestServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"Test\",\n\t\t\tHandler:    _Test_Test_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"test.proto\",\n}\n"
  },
  {
    "path": "transport/grpc/_grpc_test/request_response.go",
    "content": "package test\n\nimport (\n\t\"context\"\n\n\t\"github.com/go-kit/kit/transport/grpc/_grpc_test/pb\"\n)\n\nfunc encodeRequest(ctx context.Context, req interface{}) (interface{}, error) {\n\tr := req.(TestRequest)\n\treturn &pb.TestRequest{A: r.A, B: r.B}, nil\n}\n\nfunc decodeRequest(ctx context.Context, req interface{}) (interface{}, error) {\n\tr := req.(*pb.TestRequest)\n\treturn TestRequest{A: r.A, B: r.B}, nil\n}\n\nfunc encodeResponse(ctx context.Context, resp interface{}) (interface{}, error) {\n\tr := resp.(*TestResponse)\n\treturn &pb.TestResponse{V: r.V}, nil\n}\n\nfunc decodeResponse(ctx context.Context, resp interface{}) (interface{}, error) {\n\tr := resp.(*pb.TestResponse)\n\treturn &TestResponse{V: r.V, Ctx: ctx}, nil\n}\n"
  },
  {
    "path": "transport/grpc/_grpc_test/server.go",
    "content": "package test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\tgrpctransport \"github.com/go-kit/kit/transport/grpc\"\n\t\"github.com/go-kit/kit/transport/grpc/_grpc_test/pb\"\n)\n\ntype service struct{}\n\nfunc (service) Test(ctx context.Context, a string, b int64) (context.Context, string, error) {\n\treturn nil, fmt.Sprintf(\"%s = %d\", a, b), nil\n}\n\nfunc NewService() Service {\n\treturn service{}\n}\n\nfunc makeTestEndpoint(svc Service) endpoint.Endpoint {\n\treturn func(ctx context.Context, request interface{}) (interface{}, error) {\n\t\treq := request.(TestRequest)\n\t\tnewCtx, v, err := svc.Test(ctx, req.A, req.B)\n\t\treturn &TestResponse{\n\t\t\tV:   v,\n\t\t\tCtx: newCtx,\n\t\t}, err\n\t}\n}\n\ntype serverBinding struct {\n\tpb.UnimplementedTestServer\n\n\ttest grpctransport.Handler\n}\n\nfunc (b *serverBinding) Test(ctx context.Context, req *pb.TestRequest) (*pb.TestResponse, error) {\n\t_, response, err := b.test.ServeGRPC(ctx, req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn response.(*pb.TestResponse), nil\n}\n\nfunc NewBinding(svc Service) *serverBinding {\n\treturn &serverBinding{\n\t\ttest: grpctransport.NewServer(\n\t\t\tmakeTestEndpoint(svc),\n\t\t\tdecodeRequest,\n\t\t\tencodeResponse,\n\t\t\tgrpctransport.ServerBefore(\n\t\t\t\textractCorrelationID,\n\t\t\t),\n\t\t\tgrpctransport.ServerBefore(\n\t\t\t\tdisplayServerRequestHeaders,\n\t\t\t),\n\t\t\tgrpctransport.ServerAfter(\n\t\t\t\tinjectResponseHeader,\n\t\t\t\tinjectResponseTrailer,\n\t\t\t\tinjectConsumedCorrelationID,\n\t\t\t),\n\t\t\tgrpctransport.ServerAfter(\n\t\t\t\tdisplayServerResponseHeaders,\n\t\t\t\tdisplayServerResponseTrailers,\n\t\t\t),\n\t\t),\n\t}\n}\n"
  },
  {
    "path": "transport/grpc/_grpc_test/service.go",
    "content": "package test\n\nimport \"context\"\n\ntype Service interface {\n\tTest(ctx context.Context, a string, b int64) (context.Context, string, error)\n}\n\ntype TestRequest struct {\n\tA string\n\tB int64\n}\n\ntype TestResponse struct {\n\tCtx context.Context\n\tV   string\n}\n"
  },
  {
    "path": "transport/grpc/client.go",
    "content": "package grpc\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"reflect\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/metadata\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n)\n\n// Client wraps a gRPC connection and provides a method that implements\n// endpoint.Endpoint.\ntype Client struct {\n\tclient      *grpc.ClientConn\n\tserviceName string\n\tmethod      string\n\tenc         EncodeRequestFunc\n\tdec         DecodeResponseFunc\n\tgrpcReply   reflect.Type\n\tbefore      []ClientRequestFunc\n\tafter       []ClientResponseFunc\n\tfinalizer   []ClientFinalizerFunc\n}\n\n// NewClient constructs a usable Client for a single remote endpoint.\n// Pass an zero-value protobuf message of the RPC response type as\n// the grpcReply argument.\nfunc NewClient(\n\tcc *grpc.ClientConn,\n\tserviceName string,\n\tmethod string,\n\tenc EncodeRequestFunc,\n\tdec DecodeResponseFunc,\n\tgrpcReply interface{},\n\toptions ...ClientOption,\n) *Client {\n\tc := &Client{\n\t\tclient: cc,\n\t\tmethod: fmt.Sprintf(\"/%s/%s\", serviceName, method),\n\t\tenc:    enc,\n\t\tdec:    dec,\n\t\t// We are using reflect.Indirect here to allow both reply structs and\n\t\t// pointers to these reply structs. New consumers of the client should\n\t\t// use structs directly, while existing consumers will not break if they\n\t\t// remain to use pointers to structs.\n\t\tgrpcReply: reflect.TypeOf(\n\t\t\treflect.Indirect(\n\t\t\t\treflect.ValueOf(grpcReply),\n\t\t\t).Interface(),\n\t\t),\n\t\tbefore: []ClientRequestFunc{},\n\t\tafter:  []ClientResponseFunc{},\n\t}\n\tfor _, option := range options {\n\t\toption(c)\n\t}\n\treturn c\n}\n\n// ClientOption sets an optional parameter for clients.\ntype ClientOption func(*Client)\n\n// ClientBefore sets the RequestFuncs that are applied to the outgoing gRPC\n// request before it's invoked.\nfunc ClientBefore(before ...ClientRequestFunc) ClientOption {\n\treturn func(c *Client) { c.before = append(c.before, before...) }\n}\n\n// ClientAfter sets the ClientResponseFuncs that are applied to the incoming\n// gRPC response prior to it being decoded. This is useful for obtaining\n// response metadata and adding onto the context prior to decoding.\nfunc ClientAfter(after ...ClientResponseFunc) ClientOption {\n\treturn func(c *Client) { c.after = append(c.after, after...) }\n}\n\n// ClientFinalizer is executed at the end of every gRPC request.\n// By default, no finalizer is registered.\nfunc ClientFinalizer(f ...ClientFinalizerFunc) ClientOption {\n\treturn func(s *Client) { s.finalizer = append(s.finalizer, f...) }\n}\n\n// Endpoint returns a usable endpoint that will invoke the gRPC specified by the\n// client.\nfunc (c Client) Endpoint() endpoint.Endpoint {\n\treturn func(ctx context.Context, request interface{}) (response interface{}, err error) {\n\t\tctx, cancel := context.WithCancel(ctx)\n\t\tdefer cancel()\n\n\t\tif c.finalizer != nil {\n\t\t\tdefer func() {\n\t\t\t\tfor _, f := range c.finalizer {\n\t\t\t\t\tf(ctx, err)\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\n\t\tctx = context.WithValue(ctx, ContextKeyRequestMethod, c.method)\n\n\t\treq, err := c.enc(ctx, request)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tmd := &metadata.MD{}\n\t\tfor _, f := range c.before {\n\t\t\tctx = f(ctx, md)\n\t\t}\n\t\tctx = metadata.NewOutgoingContext(ctx, *md)\n\n\t\tvar header, trailer metadata.MD\n\t\tgrpcReply := reflect.New(c.grpcReply).Interface()\n\t\tif err = c.client.Invoke(\n\t\t\tctx, c.method, req, grpcReply, grpc.Header(&header),\n\t\t\tgrpc.Trailer(&trailer),\n\t\t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tfor _, f := range c.after {\n\t\t\tctx = f(ctx, header, trailer)\n\t\t}\n\n\t\tresponse, err = c.dec(ctx, grpcReply)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn response, nil\n\t}\n}\n\n// ClientFinalizerFunc can be used to perform work at the end of a client gRPC\n// request, after the response is returned. The principal\n// intended use is for error logging. Additional response parameters are\n// provided in the context under keys with the ContextKeyResponse prefix.\n// Note: err may be nil. There maybe also no additional response parameters depending on\n// when an error occurs.\ntype ClientFinalizerFunc func(ctx context.Context, err error)\n"
  },
  {
    "path": "transport/grpc/client_test.go",
    "content": "package grpc_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"testing\"\n\n\t\"google.golang.org/grpc\"\n\n\ttest \"github.com/go-kit/kit/transport/grpc/_grpc_test\"\n\t\"github.com/go-kit/kit/transport/grpc/_grpc_test/pb\"\n)\n\nconst (\n\thostPort string = \"localhost:8002\"\n)\n\nfunc TestGRPCClient(t *testing.T) {\n\tvar (\n\t\tserver  = grpc.NewServer()\n\t\tservice = test.NewService()\n\t)\n\n\tsc, err := net.Listen(\"tcp\", hostPort)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to listen: %+v\", err)\n\t}\n\tdefer server.GracefulStop()\n\n\tgo func() {\n\t\tpb.RegisterTestServer(server, test.NewBinding(service))\n\t\t_ = server.Serve(sc)\n\t}()\n\n\tcc, err := grpc.Dial(hostPort, grpc.WithInsecure())\n\tif err != nil {\n\t\tt.Fatalf(\"unable to Dial: %+v\", err)\n\t}\n\n\tclient := test.NewClient(cc)\n\n\tvar (\n\t\ta   = \"the answer to life the universe and everything\"\n\t\tb   = int64(42)\n\t\tcID = \"request-1\"\n\t\tctx = test.SetCorrelationID(context.Background(), cID)\n\t)\n\n\tresponseCTX, v, err := client.Test(ctx, a, b)\n\tif err != nil {\n\t\tt.Fatalf(\"unable to Test: %+v\", err)\n\t}\n\tif want, have := fmt.Sprintf(\"%s = %d\", a, b), v; want != have {\n\t\tt.Fatalf(\"want %q, have %q\", want, have)\n\t}\n\n\tif want, have := cID, test.GetConsumedCorrelationID(responseCTX); want != have {\n\t\tt.Fatalf(\"want %q, have %q\", want, have)\n\t}\n}\n"
  },
  {
    "path": "transport/grpc/doc.go",
    "content": "// Package grpc provides a gRPC binding for endpoints.\npackage grpc\n"
  },
  {
    "path": "transport/grpc/encode_decode.go",
    "content": "package grpc\n\nimport (\n\t\"context\"\n)\n\n// DecodeRequestFunc extracts a user-domain request object from a gRPC request.\n// It's designed to be used in gRPC servers, for server-side endpoints. One\n// straightforward DecodeRequestFunc could be something that decodes from the\n// gRPC request message to the concrete request type.\ntype DecodeRequestFunc func(context.Context, interface{}) (request interface{}, err error)\n\n// EncodeRequestFunc encodes the passed request object into the gRPC request\n// object. It's designed to be used in gRPC clients, for client-side endpoints.\n// One straightforward EncodeRequestFunc could something that encodes the object\n// directly to the gRPC request message.\ntype EncodeRequestFunc func(context.Context, interface{}) (request interface{}, err error)\n\n// EncodeResponseFunc encodes the passed response object to the gRPC response\n// message. It's designed to be used in gRPC servers, for server-side endpoints.\n// One straightforward EncodeResponseFunc could be something that encodes the\n// object directly to the gRPC response message.\ntype EncodeResponseFunc func(context.Context, interface{}) (response interface{}, err error)\n\n// DecodeResponseFunc extracts a user-domain response object from a gRPC\n// response object. It's designed to be used in gRPC clients, for client-side\n// endpoints. One straightforward DecodeResponseFunc could be something that\n// decodes from the gRPC response message to the concrete response type.\ntype DecodeResponseFunc func(context.Context, interface{}) (response interface{}, err error)\n"
  },
  {
    "path": "transport/grpc/request_response_funcs.go",
    "content": "package grpc\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"strings\"\n\n\t\"google.golang.org/grpc/metadata\"\n)\n\nconst (\n\tbinHdrSuffix = \"-bin\"\n)\n\n// ClientRequestFunc may take information from context and use it to construct\n// metadata headers to be transported to the server. ClientRequestFuncs are\n// executed after creating the request but prior to sending the gRPC request to\n// the server.\ntype ClientRequestFunc func(context.Context, *metadata.MD) context.Context\n\n// ServerRequestFunc may take information from the received metadata header and\n// use it to place items in the request scoped context. ServerRequestFuncs are\n// executed prior to invoking the endpoint.\ntype ServerRequestFunc func(context.Context, metadata.MD) context.Context\n\n// ServerResponseFunc may take information from a request context and use it to\n// manipulate the gRPC response metadata headers and trailers. ResponseFuncs are\n// only executed in servers, after invoking the endpoint but prior to writing a\n// response.\ntype ServerResponseFunc func(ctx context.Context, header *metadata.MD, trailer *metadata.MD) context.Context\n\n// ClientResponseFunc may take information from a gRPC metadata header and/or\n// trailer and make the responses available for consumption. ClientResponseFuncs\n// are only executed in clients, after a request has been made, but prior to it\n// being decoded.\ntype ClientResponseFunc func(ctx context.Context, header metadata.MD, trailer metadata.MD) context.Context\n\n// SetRequestHeader returns a ClientRequestFunc that sets the specified metadata\n// key-value pair.\nfunc SetRequestHeader(key, val string) ClientRequestFunc {\n\treturn func(ctx context.Context, md *metadata.MD) context.Context {\n\t\tkey, val := EncodeKeyValue(key, val)\n\t\t(*md)[key] = append((*md)[key], val)\n\t\treturn ctx\n\t}\n}\n\n// SetResponseHeader returns a ResponseFunc that sets the specified metadata\n// key-value pair.\nfunc SetResponseHeader(key, val string) ServerResponseFunc {\n\treturn func(ctx context.Context, md *metadata.MD, _ *metadata.MD) context.Context {\n\t\tkey, val := EncodeKeyValue(key, val)\n\t\t(*md)[key] = append((*md)[key], val)\n\t\treturn ctx\n\t}\n}\n\n// SetResponseTrailer returns a ResponseFunc that sets the specified metadata\n// key-value pair.\nfunc SetResponseTrailer(key, val string) ServerResponseFunc {\n\treturn func(ctx context.Context, _ *metadata.MD, md *metadata.MD) context.Context {\n\t\tkey, val := EncodeKeyValue(key, val)\n\t\t(*md)[key] = append((*md)[key], val)\n\t\treturn ctx\n\t}\n}\n\n// EncodeKeyValue sanitizes a key-value pair for use in gRPC metadata headers.\nfunc EncodeKeyValue(key, val string) (string, string) {\n\tkey = strings.ToLower(key)\n\tif strings.HasSuffix(key, binHdrSuffix) {\n\t\tval = base64.StdEncoding.EncodeToString([]byte(val))\n\t}\n\treturn key, val\n}\n\ntype contextKey int\n\nconst (\n\tContextKeyRequestMethod contextKey = iota\n)\n"
  },
  {
    "path": "transport/grpc/server.go",
    "content": "package grpc\n\nimport (\n\t\"context\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/metadata\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/transport\"\n\t\"github.com/go-kit/log\"\n)\n\n// Handler which should be called from the gRPC binding of the service\n// implementation. The incoming request parameter, and returned response\n// parameter, are both gRPC types, not user-domain.\ntype Handler interface {\n\tServeGRPC(ctx context.Context, request interface{}) (context.Context, interface{}, error)\n}\n\n// Server wraps an endpoint and implements grpc.Handler.\ntype Server struct {\n\te            endpoint.Endpoint\n\tdec          DecodeRequestFunc\n\tenc          EncodeResponseFunc\n\tbefore       []ServerRequestFunc\n\tafter        []ServerResponseFunc\n\tfinalizer    []ServerFinalizerFunc\n\terrorHandler transport.ErrorHandler\n}\n\n// NewServer constructs a new server, which implements wraps the provided\n// endpoint and implements the Handler interface. Consumers should write\n// bindings that adapt the concrete gRPC methods from their compiled protobuf\n// definitions to individual handlers. Request and response objects are from the\n// caller business domain, not gRPC request and reply types.\nfunc NewServer(\n\te endpoint.Endpoint,\n\tdec DecodeRequestFunc,\n\tenc EncodeResponseFunc,\n\toptions ...ServerOption,\n) *Server {\n\ts := &Server{\n\t\te:            e,\n\t\tdec:          dec,\n\t\tenc:          enc,\n\t\terrorHandler: transport.NewLogErrorHandler(log.NewNopLogger()),\n\t}\n\tfor _, option := range options {\n\t\toption(s)\n\t}\n\treturn s\n}\n\n// ServerOption sets an optional parameter for servers.\ntype ServerOption func(*Server)\n\n// ServerBefore functions are executed on the gRPC request object before the\n// request is decoded.\nfunc ServerBefore(before ...ServerRequestFunc) ServerOption {\n\treturn func(s *Server) { s.before = append(s.before, before...) }\n}\n\n// ServerAfter functions are executed on the gRPC response writer after the\n// endpoint is invoked, but before anything is written to the client.\nfunc ServerAfter(after ...ServerResponseFunc) ServerOption {\n\treturn func(s *Server) { s.after = append(s.after, after...) }\n}\n\n// ServerErrorLogger is used to log non-terminal errors. By default, no errors\n// are logged.\n// Deprecated: Use ServerErrorHandler instead.\nfunc ServerErrorLogger(logger log.Logger) ServerOption {\n\treturn func(s *Server) { s.errorHandler = transport.NewLogErrorHandler(logger) }\n}\n\n// ServerErrorHandler is used to handle non-terminal errors. By default, non-terminal errors\n// are ignored.\nfunc ServerErrorHandler(errorHandler transport.ErrorHandler) ServerOption {\n\treturn func(s *Server) { s.errorHandler = errorHandler }\n}\n\n// ServerFinalizer is executed at the end of every gRPC request.\n// By default, no finalizer is registered.\nfunc ServerFinalizer(f ...ServerFinalizerFunc) ServerOption {\n\treturn func(s *Server) { s.finalizer = append(s.finalizer, f...) }\n}\n\n// ServeGRPC implements the Handler interface.\nfunc (s Server) ServeGRPC(ctx context.Context, req interface{}) (retctx context.Context, resp interface{}, err error) {\n\t// Retrieve gRPC metadata.\n\tmd, ok := metadata.FromIncomingContext(ctx)\n\tif !ok {\n\t\tmd = metadata.MD{}\n\t}\n\n\tif len(s.finalizer) > 0 {\n\t\tdefer func() {\n\t\t\tfor _, f := range s.finalizer {\n\t\t\t\tf(ctx, err)\n\t\t\t}\n\t\t}()\n\t}\n\n\tfor _, f := range s.before {\n\t\tctx = f(ctx, md)\n\t}\n\n\tvar (\n\t\trequest  interface{}\n\t\tresponse interface{}\n\t\tgrpcResp interface{}\n\t)\n\n\trequest, err = s.dec(ctx, req)\n\tif err != nil {\n\t\ts.errorHandler.Handle(ctx, err)\n\t\treturn ctx, nil, err\n\t}\n\n\tresponse, err = s.e(ctx, request)\n\tif err != nil {\n\t\ts.errorHandler.Handle(ctx, err)\n\t\treturn ctx, nil, err\n\t}\n\n\tvar mdHeader, mdTrailer metadata.MD\n\tfor _, f := range s.after {\n\t\tctx = f(ctx, &mdHeader, &mdTrailer)\n\t}\n\n\tgrpcResp, err = s.enc(ctx, response)\n\tif err != nil {\n\t\ts.errorHandler.Handle(ctx, err)\n\t\treturn ctx, nil, err\n\t}\n\n\tif len(mdHeader) > 0 {\n\t\tif err = grpc.SendHeader(ctx, mdHeader); err != nil {\n\t\t\ts.errorHandler.Handle(ctx, err)\n\t\t\treturn ctx, nil, err\n\t\t}\n\t}\n\n\tif len(mdTrailer) > 0 {\n\t\tif err = grpc.SetTrailer(ctx, mdTrailer); err != nil {\n\t\t\ts.errorHandler.Handle(ctx, err)\n\t\t\treturn ctx, nil, err\n\t\t}\n\t}\n\n\treturn ctx, grpcResp, nil\n}\n\n// ServerFinalizerFunc can be used to perform work at the end of an gRPC\n// request, after the response has been written to the client.\ntype ServerFinalizerFunc func(ctx context.Context, err error)\n\n// Interceptor is a grpc UnaryInterceptor that injects the method name into\n// context so it can be consumed by Go kit gRPC middlewares. The Interceptor\n// typically is added at creation time of the grpc-go server.\n// Like this: `grpc.NewServer(grpc.UnaryInterceptor(kitgrpc.Interceptor))`\nfunc Interceptor(\n\tctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler,\n) (resp interface{}, err error) {\n\tctx = context.WithValue(ctx, ContextKeyRequestMethod, info.FullMethod)\n\treturn handler(ctx, req)\n}\n"
  },
  {
    "path": "transport/http/client.go",
    "content": "package http\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"encoding/xml\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n)\n\n// HTTPClient is an interface that models *http.Client.\ntype HTTPClient interface {\n\tDo(req *http.Request) (*http.Response, error)\n}\n\n// Client wraps a URL and provides a method that implements endpoint.Endpoint.\ntype Client struct {\n\tclient         HTTPClient\n\treq            CreateRequestFunc\n\tdec            DecodeResponseFunc\n\tbefore         []RequestFunc\n\tafter          []ClientResponseFunc\n\tfinalizer      []ClientFinalizerFunc\n\tbufferedStream bool\n}\n\n// NewClient constructs a usable Client for a single remote method.\nfunc NewClient(method string, tgt *url.URL, enc EncodeRequestFunc, dec DecodeResponseFunc, options ...ClientOption) *Client {\n\treturn NewExplicitClient(makeCreateRequestFunc(method, tgt, enc), dec, options...)\n}\n\n// NewExplicitClient is like NewClient but uses a CreateRequestFunc instead of a\n// method, target URL, and EncodeRequestFunc, which allows for more control over\n// the outgoing HTTP request.\nfunc NewExplicitClient(req CreateRequestFunc, dec DecodeResponseFunc, options ...ClientOption) *Client {\n\tc := &Client{\n\t\tclient: http.DefaultClient,\n\t\treq:    req,\n\t\tdec:    dec,\n\t}\n\tfor _, option := range options {\n\t\toption(c)\n\t}\n\treturn c\n}\n\n// ClientOption sets an optional parameter for clients.\ntype ClientOption func(*Client)\n\n// SetClient sets the underlying HTTP client used for requests.\n// By default, http.DefaultClient is used.\nfunc SetClient(client HTTPClient) ClientOption {\n\treturn func(c *Client) { c.client = client }\n}\n\n// ClientBefore adds one or more RequestFuncs to be applied to the outgoing HTTP\n// request before it's invoked.\nfunc ClientBefore(before ...RequestFunc) ClientOption {\n\treturn func(c *Client) { c.before = append(c.before, before...) }\n}\n\n// ClientAfter adds one or more ClientResponseFuncs, which are applied to the\n// incoming HTTP response prior to it being decoded. This is useful for\n// obtaining anything off of the response and adding it into the context prior\n// to decoding.\nfunc ClientAfter(after ...ClientResponseFunc) ClientOption {\n\treturn func(c *Client) { c.after = append(c.after, after...) }\n}\n\n// ClientFinalizer adds one or more ClientFinalizerFuncs to be executed at the\n// end of every HTTP request. Finalizers are executed in the order in which they\n// were added. By default, no finalizer is registered.\nfunc ClientFinalizer(f ...ClientFinalizerFunc) ClientOption {\n\treturn func(s *Client) { s.finalizer = append(s.finalizer, f...) }\n}\n\n// BufferedStream sets whether the HTTP response body is left open, allowing it\n// to be read from later. Useful for transporting a file as a buffered stream.\n// That body has to be drained and closed to properly end the request.\nfunc BufferedStream(buffered bool) ClientOption {\n\treturn func(c *Client) { c.bufferedStream = buffered }\n}\n\n// Endpoint returns a usable Go kit endpoint that calls the remote HTTP endpoint.\nfunc (c Client) Endpoint() endpoint.Endpoint {\n\treturn func(ctx context.Context, request interface{}) (interface{}, error) {\n\t\tctx, cancel := context.WithCancel(ctx)\n\n\t\tvar (\n\t\t\tresp *http.Response\n\t\t\terr  error\n\t\t)\n\t\tif c.finalizer != nil {\n\t\t\tdefer func() {\n\t\t\t\tif resp != nil {\n\t\t\t\t\tctx = context.WithValue(ctx, ContextKeyResponseHeaders, resp.Header)\n\t\t\t\t\tctx = context.WithValue(ctx, ContextKeyResponseSize, resp.ContentLength)\n\t\t\t\t}\n\t\t\t\tfor _, f := range c.finalizer {\n\t\t\t\t\tf(ctx, err)\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\n\t\treq, err := c.req(ctx, request)\n\t\tif err != nil {\n\t\t\tcancel()\n\t\t\treturn nil, err\n\t\t}\n\n\t\tfor _, f := range c.before {\n\t\t\tctx = f(ctx, req)\n\t\t}\n\n\t\tresp, err = c.client.Do(req.WithContext(ctx))\n\t\tif err != nil {\n\t\t\tcancel()\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// If the caller asked for a buffered stream, we don't cancel the\n\t\t// context when the endpoint returns. Instead, we should call the\n\t\t// cancel func when closing the response body.\n\t\tif c.bufferedStream {\n\t\t\tresp.Body = bodyWithCancel{ReadCloser: resp.Body, cancel: cancel}\n\t\t} else {\n\t\t\tdefer resp.Body.Close()\n\t\t\tdefer cancel()\n\t\t}\n\n\t\tfor _, f := range c.after {\n\t\t\tctx = f(ctx, resp)\n\t\t}\n\n\t\tresponse, err := c.dec(ctx, resp)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn response, nil\n\t}\n}\n\n// bodyWithCancel is a wrapper for an io.ReadCloser with also a\n// cancel function which is called when the Close is used\ntype bodyWithCancel struct {\n\tio.ReadCloser\n\n\tcancel context.CancelFunc\n}\n\nfunc (bwc bodyWithCancel) Close() error {\n\tbwc.ReadCloser.Close()\n\tbwc.cancel()\n\treturn nil\n}\n\n// ClientFinalizerFunc can be used to perform work at the end of a client HTTP\n// request, after the response is returned. The principal\n// intended use is for error logging. Additional response parameters are\n// provided in the context under keys with the ContextKeyResponse prefix.\n// Note: err may be nil. There maybe also no additional response parameters\n// depending on when an error occurs.\ntype ClientFinalizerFunc func(ctx context.Context, err error)\n\n// EncodeJSONRequest is an EncodeRequestFunc that serializes the request as a\n// JSON object to the Request body. Many JSON-over-HTTP services can use it as\n// a sensible default. If the request implements Headerer, the provided headers\n// will be applied to the request.\nfunc EncodeJSONRequest(c context.Context, r *http.Request, request interface{}) error {\n\tr.Header.Set(\"Content-Type\", \"application/json; charset=utf-8\")\n\tif headerer, ok := request.(Headerer); ok {\n\t\tfor k := range headerer.Headers() {\n\t\t\tr.Header.Set(k, headerer.Headers().Get(k))\n\t\t}\n\t}\n\tvar b bytes.Buffer\n\tr.Body = ioutil.NopCloser(&b)\n\treturn json.NewEncoder(&b).Encode(request)\n}\n\n// EncodeXMLRequest is an EncodeRequestFunc that serializes the request as a\n// XML object to the Request body. If the request implements Headerer,\n// the provided headers will be applied to the request.\nfunc EncodeXMLRequest(c context.Context, r *http.Request, request interface{}) error {\n\tr.Header.Set(\"Content-Type\", \"text/xml; charset=utf-8\")\n\tif headerer, ok := request.(Headerer); ok {\n\t\tfor k := range headerer.Headers() {\n\t\t\tr.Header.Set(k, headerer.Headers().Get(k))\n\t\t}\n\t}\n\tvar b bytes.Buffer\n\tr.Body = ioutil.NopCloser(&b)\n\treturn xml.NewEncoder(&b).Encode(request)\n}\n\n//\n//\n//\n\nfunc makeCreateRequestFunc(method string, target *url.URL, enc EncodeRequestFunc) CreateRequestFunc {\n\treturn func(ctx context.Context, request interface{}) (*http.Request, error) {\n\t\treq, err := http.NewRequest(method, target.String(), nil)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif err = enc(ctx, req, request); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn req, nil\n\t}\n}\n"
  },
  {
    "path": "transport/http/client_test.go",
    "content": "package http_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\thttptransport \"github.com/go-kit/kit/transport/http\"\n)\n\ntype TestResponse struct {\n\tBody   io.ReadCloser\n\tString string\n}\n\nfunc TestHTTPClient(t *testing.T) {\n\tvar (\n\t\ttestbody = \"testbody\"\n\t\tencode   = func(context.Context, *http.Request, interface{}) error { return nil }\n\t\tdecode   = func(_ context.Context, r *http.Response) (interface{}, error) {\n\t\t\tbuffer := make([]byte, len(testbody))\n\t\t\tr.Body.Read(buffer)\n\t\t\treturn TestResponse{r.Body, string(buffer)}, nil\n\t\t}\n\t\theaders        = make(chan string, 1)\n\t\theaderKey      = \"X-Foo\"\n\t\theaderVal      = \"abcde\"\n\t\tafterHeaderKey = \"X-The-Dude\"\n\t\tafterHeaderVal = \"Abides\"\n\t\tafterVal       = \"\"\n\t\tafterFunc      = func(ctx context.Context, r *http.Response) context.Context {\n\t\t\tafterVal = r.Header.Get(afterHeaderKey)\n\t\t\treturn ctx\n\t\t}\n\t)\n\n\tserver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\theaders <- r.Header.Get(headerKey)\n\t\tw.Header().Set(afterHeaderKey, afterHeaderVal)\n\t\tw.WriteHeader(http.StatusOK)\n\t\tw.Write([]byte(testbody))\n\t}))\n\n\tclient := httptransport.NewClient(\n\t\t\"GET\",\n\t\tmustParse(server.URL),\n\t\tencode,\n\t\tdecode,\n\t\thttptransport.ClientBefore(httptransport.SetRequestHeader(headerKey, headerVal)),\n\t\thttptransport.ClientAfter(afterFunc),\n\t)\n\n\tres, err := client.Endpoint()(context.Background(), struct{}{})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar have string\n\tselect {\n\tcase have = <-headers:\n\tcase <-time.After(time.Millisecond):\n\t\tt.Fatalf(\"timeout waiting for %s\", headerKey)\n\t}\n\t// Check that Request Header was successfully received\n\tif want := headerVal; want != have {\n\t\tt.Errorf(\"want %q, have %q\", want, have)\n\t}\n\n\t// Check that Response header set from server was received in SetClientAfter\n\tif want, have := afterVal, afterHeaderVal; want != have {\n\t\tt.Errorf(\"want %q, have %q\", want, have)\n\t}\n\n\t// Check that the response was successfully decoded\n\tresponse, ok := res.(TestResponse)\n\tif !ok {\n\t\tt.Fatal(\"response should be TestResponse\")\n\t}\n\tif want, have := testbody, response.String; want != have {\n\t\tt.Errorf(\"want %q, have %q\", want, have)\n\t}\n\n\t// Check that response body was closed\n\tb := make([]byte, 1)\n\t_, err = response.Body.Read(b)\n\tif err == nil {\n\t\tt.Fatal(\"wanted error, got none\")\n\t}\n\tif doNotWant, have := io.EOF, err; doNotWant == have {\n\t\tt.Errorf(\"do not want %q, have %q\", doNotWant, have)\n\t}\n}\n\nfunc TestHTTPClientBufferedStream(t *testing.T) {\n\t// bodysize has a size big enought to make the resopnse.Body not an instant read\n\t// so if the response is cancelled it wount be all readed and the test would fail\n\t// The 6000 has not a particular meaning, it big enough to fulfill the usecase.\n\tconst bodysize = 6000\n\tvar (\n\t\ttestbody = string(make([]byte, bodysize))\n\t\tencode   = func(context.Context, *http.Request, interface{}) error { return nil }\n\t\tdecode   = func(_ context.Context, r *http.Response) (interface{}, error) {\n\t\t\treturn TestResponse{r.Body, \"\"}, nil\n\t\t}\n\t)\n\n\tserver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(http.StatusOK)\n\t\tw.Write([]byte(testbody))\n\t}))\n\n\tclient := httptransport.NewClient(\n\t\t\"GET\",\n\t\tmustParse(server.URL),\n\t\tencode,\n\t\tdecode,\n\t\thttptransport.BufferedStream(true),\n\t)\n\n\tres, err := client.Endpoint()(context.Background(), struct{}{})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Check that the response was successfully decoded\n\tresponse, ok := res.(TestResponse)\n\tif !ok {\n\t\tt.Fatal(\"response should be TestResponse\")\n\t}\n\tdefer response.Body.Close()\n\t// Faking work\n\ttime.Sleep(time.Second * 1)\n\n\t// Check that response body was NOT closed\n\tb := make([]byte, len(testbody))\n\t_, err = response.Body.Read(b)\n\tif want, have := io.EOF, err; have != want {\n\t\tt.Fatalf(\"want %q, have %q\", want, have)\n\t}\n\tif want, have := testbody, string(b); want != have {\n\t\tt.Errorf(\"want %q, have %q\", want, have)\n\t}\n}\n\nfunc TestClientFinalizer(t *testing.T) {\n\tvar (\n\t\theaderKey    = \"X-Henlo-Lizer\"\n\t\theaderVal    = \"Helllo you stinky lizard\"\n\t\tresponseBody = \"go eat a fly ugly\\n\"\n\t\tdone         = make(chan struct{})\n\t\tencode       = func(context.Context, *http.Request, interface{}) error { return nil }\n\t\tdecode       = func(_ context.Context, r *http.Response) (interface{}, error) {\n\t\t\treturn TestResponse{r.Body, \"\"}, nil\n\t\t}\n\t)\n\n\tserver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.Header().Set(headerKey, headerVal)\n\t\tw.Write([]byte(responseBody))\n\t}))\n\tdefer server.Close()\n\n\tclient := httptransport.NewClient(\n\t\t\"GET\",\n\t\tmustParse(server.URL),\n\t\tencode,\n\t\tdecode,\n\t\thttptransport.ClientFinalizer(func(ctx context.Context, err error) {\n\t\t\tresponseHeader := ctx.Value(httptransport.ContextKeyResponseHeaders).(http.Header)\n\t\t\tif want, have := headerVal, responseHeader.Get(headerKey); want != have {\n\t\t\t\tt.Errorf(\"%s: want %q, have %q\", headerKey, want, have)\n\t\t\t}\n\n\t\t\tresponseSize := ctx.Value(httptransport.ContextKeyResponseSize).(int64)\n\t\t\tif want, have := int64(len(responseBody)), responseSize; want != have {\n\t\t\t\tt.Errorf(\"response size: want %d, have %d\", want, have)\n\t\t\t}\n\n\t\t\tclose(done)\n\t\t}),\n\t)\n\n\t_, err := client.Endpoint()(context.Background(), struct{}{})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tselect {\n\tcase <-done:\n\tcase <-time.After(time.Second):\n\t\tt.Fatal(\"timeout waiting for finalizer\")\n\t}\n}\n\nfunc TestEncodeJSONRequest(t *testing.T) {\n\tvar header http.Header\n\tvar body string\n\n\tserver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tb, err := ioutil.ReadAll(r.Body)\n\t\tif err != nil && err != io.EOF {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\theader = r.Header\n\t\tbody = string(b)\n\t}))\n\n\tdefer server.Close()\n\n\tserverURL, err := url.Parse(server.URL)\n\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tclient := httptransport.NewClient(\n\t\t\"POST\",\n\t\tserverURL,\n\t\thttptransport.EncodeJSONRequest,\n\t\tfunc(context.Context, *http.Response) (interface{}, error) { return nil, nil },\n\t).Endpoint()\n\n\tfor _, test := range []struct {\n\t\tvalue interface{}\n\t\tbody  string\n\t}{\n\t\t{nil, \"null\\n\"},\n\t\t{12, \"12\\n\"},\n\t\t{1.2, \"1.2\\n\"},\n\t\t{true, \"true\\n\"},\n\t\t{\"test\", \"\\\"test\\\"\\n\"},\n\t\t{enhancedRequest{Foo: \"foo\"}, \"{\\\"foo\\\":\\\"foo\\\"}\\n\"},\n\t} {\n\t\tif _, err := client(context.Background(), test.value); err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tif body != test.body {\n\t\t\tt.Errorf(\"%v: actual %#v, expected %#v\", test.value, body, test.body)\n\t\t}\n\t}\n\n\tif _, err := client(context.Background(), enhancedRequest{Foo: \"foo\"}); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif _, ok := header[\"X-Edward\"]; !ok {\n\t\tt.Fatalf(\"X-Edward value: actual %v, expected %v\", nil, []string{\"Snowden\"})\n\t}\n\n\tif v := header.Get(\"X-Edward\"); v != \"Snowden\" {\n\t\tt.Errorf(\"X-Edward string: actual %v, expected %v\", v, \"Snowden\")\n\t}\n}\n\nfunc TestSetClient(t *testing.T) {\n\tvar (\n\t\tencode = func(context.Context, *http.Request, interface{}) error { return nil }\n\t\tdecode = func(_ context.Context, r *http.Response) (interface{}, error) {\n\t\t\tt, err := ioutil.ReadAll(r.Body)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn string(t), nil\n\t\t}\n\t)\n\n\ttestHttpClient := httpClientFunc(func(req *http.Request) (*http.Response, error) {\n\t\treturn &http.Response{\n\t\t\tStatusCode: http.StatusOK,\n\t\t\tRequest:    req,\n\t\t\tBody:       ioutil.NopCloser(bytes.NewBufferString(\"hello, world!\")),\n\t\t}, nil\n\t})\n\n\tclient := httptransport.NewClient(\n\t\t\"GET\",\n\t\t&url.URL{},\n\t\tencode,\n\t\tdecode,\n\t\thttptransport.SetClient(testHttpClient),\n\t).Endpoint()\n\n\tresp, err := client(context.Background(), nil)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif r, ok := resp.(string); !ok || r != \"hello, world!\" {\n\t\tt.Fatal(\"Expected response to be 'hello, world!' string\")\n\t}\n}\n\nfunc TestNewExplicitClient(t *testing.T) {\n\tsrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tfmt.Fprintf(w, \"%d\", r.ContentLength)\n\t}))\n\tdefer srv.Close()\n\n\treq := func(ctx context.Context, request interface{}) (*http.Request, error) {\n\t\treq, _ := http.NewRequest(\"POST\", srv.URL, strings.NewReader(request.(string)))\n\t\treturn req, nil\n\t}\n\n\tdec := func(_ context.Context, resp *http.Response) (response interface{}, err error) {\n\t\tbuf, err := ioutil.ReadAll(resp.Body)\n\t\tresp.Body.Close()\n\t\treturn string(buf), err\n\t}\n\n\tclient := httptransport.NewExplicitClient(req, dec)\n\n\trequest := \"hello world\"\n\tresponse, err := client.Endpoint()(context.Background(), request)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif want, have := \"11\", response.(string); want != have {\n\t\tt.Fatalf(\"want %q, have %q\", want, have)\n\t}\n}\n\nfunc mustParse(s string) *url.URL {\n\tu, err := url.Parse(s)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn u\n}\n\ntype enhancedRequest struct {\n\tFoo string `json:\"foo\"`\n}\n\nfunc (e enhancedRequest) Headers() http.Header { return http.Header{\"X-Edward\": []string{\"Snowden\"}} }\n\ntype httpClientFunc func(req *http.Request) (*http.Response, error)\n\nfunc (f httpClientFunc) Do(req *http.Request) (*http.Response, error) {\n\treturn f(req)\n}\n"
  },
  {
    "path": "transport/http/doc.go",
    "content": "// Package http provides a general purpose HTTP binding for endpoints.\npackage http\n"
  },
  {
    "path": "transport/http/encode_decode.go",
    "content": "package http\n\nimport (\n\t\"context\"\n\t\"net/http\"\n)\n\n// DecodeRequestFunc extracts a user-domain request object from an HTTP\n// request object. It's designed to be used in HTTP servers, for server-side\n// endpoints. One straightforward DecodeRequestFunc could be something that\n// JSON decodes from the request body to the concrete request type.\ntype DecodeRequestFunc func(context.Context, *http.Request) (request interface{}, err error)\n\n// EncodeRequestFunc encodes the passed request object into the HTTP request\n// object. It's designed to be used in HTTP clients, for client-side\n// endpoints. One straightforward EncodeRequestFunc could be something that JSON\n// encodes the object directly to the request body.\ntype EncodeRequestFunc func(context.Context, *http.Request, interface{}) error\n\n// CreateRequestFunc creates an outgoing HTTP request based on the passed\n// request object. It's designed to be used in HTTP clients, for client-side\n// endpoints. It's a more powerful version of EncodeRequestFunc, and can be used\n// if more fine-grained control of the HTTP request is required.\ntype CreateRequestFunc func(context.Context, interface{}) (*http.Request, error)\n\n// EncodeResponseFunc encodes the passed response object to the HTTP response\n// writer. It's designed to be used in HTTP servers, for server-side\n// endpoints. One straightforward EncodeResponseFunc could be something that\n// JSON encodes the object directly to the response body.\ntype EncodeResponseFunc func(context.Context, http.ResponseWriter, interface{}) error\n\n// DecodeResponseFunc extracts a user-domain response object from an HTTP\n// response object. It's designed to be used in HTTP clients, for client-side\n// endpoints. One straightforward DecodeResponseFunc could be something that\n// JSON decodes from the response body to the concrete response type.\ntype DecodeResponseFunc func(context.Context, *http.Response) (response interface{}, err error)\n"
  },
  {
    "path": "transport/http/example_test.go",
    "content": "package http\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n)\n\nfunc ExamplePopulateRequestContext() {\n\thandler := NewServer(\n\t\tfunc(ctx context.Context, request interface{}) (response interface{}, err error) {\n\t\t\tfmt.Println(\"Method\", ctx.Value(ContextKeyRequestMethod).(string))\n\t\t\tfmt.Println(\"RequestPath\", ctx.Value(ContextKeyRequestPath).(string))\n\t\t\tfmt.Println(\"RequestURI\", ctx.Value(ContextKeyRequestURI).(string))\n\t\t\tfmt.Println(\"X-Request-ID\", ctx.Value(ContextKeyRequestXRequestID).(string))\n\t\t\treturn struct{}{}, nil\n\t\t},\n\t\tfunc(context.Context, *http.Request) (interface{}, error) { return struct{}{}, nil },\n\t\tfunc(context.Context, http.ResponseWriter, interface{}) error { return nil },\n\t\tServerBefore(PopulateRequestContext),\n\t)\n\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\n\treq, _ := http.NewRequest(\"PATCH\", fmt.Sprintf(\"%s/search?q=sympatico\", server.URL), nil)\n\treq.Header.Set(\"X-Request-Id\", \"a1b2c3d4e5\")\n\thttp.DefaultClient.Do(req)\n\n\t// Output:\n\t// Method PATCH\n\t// RequestPath /search\n\t// RequestURI /search?q=sympatico\n\t// X-Request-ID a1b2c3d4e5\n}\n"
  },
  {
    "path": "transport/http/intercepting_writer.go",
    "content": "package http\n\nimport (\n\t\"io\"\n\t\"net/http\"\n)\n\ntype interceptingWriter struct {\n\thttp.ResponseWriter\n\tcode    int\n\twritten int64\n}\n\n// WriteHeader may not be explicitly called, so care must be taken to\n// initialize w.code to its default value of http.StatusOK.\nfunc (w *interceptingWriter) WriteHeader(code int) {\n\tw.code = code\n\tw.ResponseWriter.WriteHeader(code)\n}\n\nfunc (w *interceptingWriter) Write(p []byte) (int, error) {\n\tn, err := w.ResponseWriter.Write(p)\n\tw.written += int64(n)\n\treturn n, err\n}\n\n// reimplementInterfaces returns a wrapped version of the embedded ResponseWriter\n// and selectively implements the same combination of additional interfaces as\n// the wrapped one. The interfaces it may implement are: http.Hijacker,\n// http.CloseNotifier, http.Pusher, http.Flusher and io.ReaderFrom. The standard\n// library is known to assert the existence of these interfaces and behaves\n// differently. This implementation is derived from\n// https://github.com/felixge/httpsnoop.\nfunc (w *interceptingWriter) reimplementInterfaces() http.ResponseWriter {\n\tvar (\n\t\thj, i0 = w.ResponseWriter.(http.Hijacker)\n\t\tcn, i1 = w.ResponseWriter.(http.CloseNotifier)\n\t\tpu, i2 = w.ResponseWriter.(http.Pusher)\n\t\tfl, i3 = w.ResponseWriter.(http.Flusher)\n\t\trf, i4 = w.ResponseWriter.(io.ReaderFrom)\n\t)\n\n\tswitch {\n\tcase !i0 && !i1 && !i2 && !i3 && !i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t}{w}\n\tcase !i0 && !i1 && !i2 && !i3 && i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\tio.ReaderFrom\n\t\t}{w, rf}\n\tcase !i0 && !i1 && !i2 && i3 && !i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Flusher\n\t\t}{w, fl}\n\tcase !i0 && !i1 && !i2 && i3 && i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Flusher\n\t\t\tio.ReaderFrom\n\t\t}{w, fl, rf}\n\tcase !i0 && !i1 && i2 && !i3 && !i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Pusher\n\t\t}{w, pu}\n\tcase !i0 && !i1 && i2 && !i3 && i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Pusher\n\t\t\tio.ReaderFrom\n\t\t}{w, pu, rf}\n\tcase !i0 && !i1 && i2 && i3 && !i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Pusher\n\t\t\thttp.Flusher\n\t\t}{w, pu, fl}\n\tcase !i0 && !i1 && i2 && i3 && i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Pusher\n\t\t\thttp.Flusher\n\t\t\tio.ReaderFrom\n\t\t}{w, pu, fl, rf}\n\tcase !i0 && i1 && !i2 && !i3 && !i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.CloseNotifier\n\t\t}{w, cn}\n\tcase !i0 && i1 && !i2 && !i3 && i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.CloseNotifier\n\t\t\tio.ReaderFrom\n\t\t}{w, cn, rf}\n\tcase !i0 && i1 && !i2 && i3 && !i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Flusher\n\t\t}{w, cn, fl}\n\tcase !i0 && i1 && !i2 && i3 && i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Flusher\n\t\t\tio.ReaderFrom\n\t\t}{w, cn, fl, rf}\n\tcase !i0 && i1 && i2 && !i3 && !i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Pusher\n\t\t}{w, cn, pu}\n\tcase !i0 && i1 && i2 && !i3 && i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Pusher\n\t\t\tio.ReaderFrom\n\t\t}{w, cn, pu, rf}\n\tcase !i0 && i1 && i2 && i3 && !i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Pusher\n\t\t\thttp.Flusher\n\t\t}{w, cn, pu, fl}\n\tcase !i0 && i1 && i2 && i3 && i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Pusher\n\t\t\thttp.Flusher\n\t\t\tio.ReaderFrom\n\t\t}{w, cn, pu, fl, rf}\n\tcase i0 && !i1 && !i2 && !i3 && !i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Hijacker\n\t\t}{w, hj}\n\tcase i0 && !i1 && !i2 && !i3 && i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Hijacker\n\t\t\tio.ReaderFrom\n\t\t}{w, hj, rf}\n\tcase i0 && !i1 && !i2 && i3 && !i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Hijacker\n\t\t\thttp.Flusher\n\t\t}{w, hj, fl}\n\tcase i0 && !i1 && !i2 && i3 && i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Hijacker\n\t\t\thttp.Flusher\n\t\t\tio.ReaderFrom\n\t\t}{w, hj, fl, rf}\n\tcase i0 && !i1 && i2 && !i3 && !i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Hijacker\n\t\t\thttp.Pusher\n\t\t}{w, hj, pu}\n\tcase i0 && !i1 && i2 && !i3 && i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Hijacker\n\t\t\thttp.Pusher\n\t\t\tio.ReaderFrom\n\t\t}{w, hj, pu, rf}\n\tcase i0 && !i1 && i2 && i3 && !i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Hijacker\n\t\t\thttp.Pusher\n\t\t\thttp.Flusher\n\t\t}{w, hj, pu, fl}\n\tcase i0 && !i1 && i2 && i3 && i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Hijacker\n\t\t\thttp.Pusher\n\t\t\thttp.Flusher\n\t\t\tio.ReaderFrom\n\t\t}{w, hj, pu, fl, rf}\n\tcase i0 && i1 && !i2 && !i3 && !i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Hijacker\n\t\t\thttp.CloseNotifier\n\t\t}{w, hj, cn}\n\tcase i0 && i1 && !i2 && !i3 && i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Hijacker\n\t\t\thttp.CloseNotifier\n\t\t\tio.ReaderFrom\n\t\t}{w, hj, cn, rf}\n\tcase i0 && i1 && !i2 && i3 && !i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Hijacker\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Flusher\n\t\t}{w, hj, cn, fl}\n\tcase i0 && i1 && !i2 && i3 && i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Hijacker\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Flusher\n\t\t\tio.ReaderFrom\n\t\t}{w, hj, cn, fl, rf}\n\tcase i0 && i1 && i2 && !i3 && !i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Hijacker\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Pusher\n\t\t}{w, hj, cn, pu}\n\tcase i0 && i1 && i2 && !i3 && i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Hijacker\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Pusher\n\t\t\tio.ReaderFrom\n\t\t}{w, hj, cn, pu, rf}\n\tcase i0 && i1 && i2 && i3 && !i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Hijacker\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Pusher\n\t\t\thttp.Flusher\n\t\t}{w, hj, cn, pu, fl}\n\tcase i0 && i1 && i2 && i3 && i4:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Hijacker\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Pusher\n\t\t\thttp.Flusher\n\t\t\tio.ReaderFrom\n\t\t}{w, hj, cn, pu, fl, rf}\n\tdefault:\n\t\treturn struct {\n\t\t\thttp.ResponseWriter\n\t\t}{w}\n\t}\n}\n"
  },
  {
    "path": "transport/http/intercepting_writer_test.go",
    "content": "package http\n\nimport (\n\t\"bufio\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"testing\"\n)\n\ntype versatileWriter struct {\n\thttp.ResponseWriter\n\tcloseNotifyCalled bool\n\thijackCalled      bool\n\treadFromCalled    bool\n\tpushCalled        bool\n\tflushCalled       bool\n}\n\nfunc (v *versatileWriter) Flush() { v.flushCalled = true }\nfunc (v *versatileWriter) Push(target string, opts *http.PushOptions) error {\n\tv.pushCalled = true\n\treturn nil\n}\nfunc (v *versatileWriter) ReadFrom(r io.Reader) (n int64, err error) {\n\tv.readFromCalled = true\n\treturn 0, nil\n}\nfunc (v *versatileWriter) CloseNotify() <-chan bool { v.closeNotifyCalled = true; return nil }\nfunc (v *versatileWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {\n\tv.hijackCalled = true\n\treturn nil, nil, nil\n}\n\nfunc TestInterceptingWriter_passthroughs(t *testing.T) {\n\tw := &versatileWriter{}\n\tiw := (&interceptingWriter{ResponseWriter: w}).reimplementInterfaces()\n\tiw.(http.Flusher).Flush()\n\tiw.(http.Pusher).Push(\"\", nil)\n\tiw.(http.CloseNotifier).CloseNotify()\n\tiw.(http.Hijacker).Hijack()\n\tiw.(io.ReaderFrom).ReadFrom(nil)\n\n\tif !w.flushCalled {\n\t\tt.Error(\"Flush not called\")\n\t}\n\tif !w.pushCalled {\n\t\tt.Error(\"Push not called\")\n\t}\n\tif !w.closeNotifyCalled {\n\t\tt.Error(\"CloseNotify not called\")\n\t}\n\tif !w.hijackCalled {\n\t\tt.Error(\"Hijack not called\")\n\t}\n\tif !w.readFromCalled {\n\t\tt.Error(\"ReadFrom not called\")\n\t}\n}\n\n// TestInterceptingWriter_reimplementInterfaces is also derived from\n// https://github.com/felixge/httpsnoop, like interceptingWriter.\nfunc TestInterceptingWriter_reimplementInterfaces(t *testing.T) {\n\t// combination 1/32\n\t{\n\t\tt.Log(\"http.ResponseWriter\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 2/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Pusher\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Pusher\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 3/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, io.ReaderFrom\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\tio.ReaderFrom\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 4/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, io.ReaderFrom, http.Pusher\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\tio.ReaderFrom\n\t\t\thttp.Pusher\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 5/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Hijacker\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Hijacker\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 6/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Hijacker, http.Pusher\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Hijacker\n\t\t\thttp.Pusher\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 7/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Hijacker, io.ReaderFrom\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Hijacker\n\t\t\tio.ReaderFrom\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 8/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Hijacker, io.ReaderFrom, http.Pusher\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Hijacker\n\t\t\tio.ReaderFrom\n\t\t\thttp.Pusher\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 9/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.CloseNotifier\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.CloseNotifier\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 10/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.CloseNotifier, http.Pusher\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Pusher\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 11/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.CloseNotifier, io.ReaderFrom\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.CloseNotifier\n\t\t\tio.ReaderFrom\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 12/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.CloseNotifier, io.ReaderFrom, http.Pusher\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.CloseNotifier\n\t\t\tio.ReaderFrom\n\t\t\thttp.Pusher\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 13/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.CloseNotifier, http.Hijacker\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Hijacker\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 14/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.CloseNotifier, http.Hijacker, http.Pusher\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Hijacker\n\t\t\thttp.Pusher\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 15/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.CloseNotifier, http.Hijacker, io.ReaderFrom\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Hijacker\n\t\t\tio.ReaderFrom\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 16/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.CloseNotifier, http.Hijacker, io.ReaderFrom, http.Pusher\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Hijacker\n\t\t\tio.ReaderFrom\n\t\t\thttp.Pusher\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 17/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Flusher\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Flusher\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 18/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Flusher, http.Pusher\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Flusher\n\t\t\thttp.Pusher\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 19/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Flusher, io.ReaderFrom\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Flusher\n\t\t\tio.ReaderFrom\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 20/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Flusher, io.ReaderFrom, http.Pusher\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Flusher\n\t\t\tio.ReaderFrom\n\t\t\thttp.Pusher\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 21/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Flusher, http.Hijacker\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Flusher\n\t\t\thttp.Hijacker\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 22/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Flusher, http.Hijacker, http.Pusher\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Flusher\n\t\t\thttp.Hijacker\n\t\t\thttp.Pusher\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 23/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Flusher, http.Hijacker, io.ReaderFrom\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Flusher\n\t\t\thttp.Hijacker\n\t\t\tio.ReaderFrom\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 24/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Flusher, http.Hijacker, io.ReaderFrom, http.Pusher\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Flusher\n\t\t\thttp.Hijacker\n\t\t\tio.ReaderFrom\n\t\t\thttp.Pusher\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 25/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Flusher, http.CloseNotifier\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Flusher\n\t\t\thttp.CloseNotifier\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 26/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Flusher, http.CloseNotifier, http.Pusher\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Flusher\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Pusher\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 27/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Flusher, http.CloseNotifier, io.ReaderFrom\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Flusher\n\t\t\thttp.CloseNotifier\n\t\t\tio.ReaderFrom\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 28/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Flusher, http.CloseNotifier, io.ReaderFrom, http.Pusher\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Flusher\n\t\t\thttp.CloseNotifier\n\t\t\tio.ReaderFrom\n\t\t\thttp.Pusher\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 29/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Flusher, http.CloseNotifier, http.Hijacker\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Flusher\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Hijacker\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 30/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Flusher, http.CloseNotifier, http.Hijacker, http.Pusher\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Flusher\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Hijacker\n\t\t\thttp.Pusher\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 31/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Flusher, http.CloseNotifier, http.Hijacker, io.ReaderFrom\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Flusher\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Hijacker\n\t\t\tio.ReaderFrom\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != false {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n\n\t// combination 32/32\n\t{\n\t\tt.Log(\"http.ResponseWriter, http.Flusher, http.CloseNotifier, http.Hijacker, io.ReaderFrom, http.Pusher\")\n\t\tinner := struct {\n\t\t\thttp.ResponseWriter\n\t\t\thttp.Flusher\n\t\t\thttp.CloseNotifier\n\t\t\thttp.Hijacker\n\t\t\tio.ReaderFrom\n\t\t\thttp.Pusher\n\t\t}{}\n\t\tw := (&interceptingWriter{ResponseWriter: inner}).reimplementInterfaces()\n\t\tif _, ok := w.(http.ResponseWriter); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Flusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.CloseNotifier); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Hijacker); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(io.ReaderFrom); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\t\tif _, ok := w.(http.Pusher); ok != true {\n\t\t\tt.Error(\"unexpected interface\")\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "transport/http/jsonrpc/README.md",
    "content": "# JSON RPC\n\n[JSON RPC](http://www.jsonrpc.org) is \"A light weight remote procedure call protocol\". It allows for the creation of simple RPC-style APIs with human-readable messages that are front-end friendly.\n\n## Using JSON RPC with Go-Kit\nUsing JSON RPC and go-kit together is quite simple.\n\nA JSON RPC _server_ acts as an [HTTP Handler](https://godoc.org/net/http#Handler), receiving all requests to the JSON RPC's URL. The server looks at the `method` property of the [Request Object](http://www.jsonrpc.org/specification#request_object), and routes it to the corresponding code.\n\nEach JSON RPC _method_ is implemented as an `EndpointCodec`, a go-kit [Endpoint](https://godoc.org/github.com/go-kit/kit/endpoint#Endpoint), sandwiched between a decoder and encoder. The decoder picks apart the JSON RPC request params, which can be passed to your endpoint. The encoder receives the output from the endpoint and encodes a JSON-RPC result.\n\n## Example — Add Service\nLet's say we want a service that adds two ints together. We'll serve this at `http://localhost/rpc`. So a request to our `sum` method will be a POST to `http://localhost/rpc` with a request body of:\n\n\t{\n\t    \"id\": 123,\n\t    \"jsonrpc\": \"2.0\",\n\t    \"method\": \"sum\",\n\t    \"params\": {\n\t    \t\"A\": 2,\n\t    \t\"B\": 2\n\t    }\n\t}\n\n### `EndpointCodecMap`\nThe routing table for incoming JSON RPC requests is the `EndpointCodecMap`. The key of the map is the JSON RPC method name. Here, we're routing the `sum` method to an `EndpointCodec` wrapped around `sumEndpoint`.\n\n\tjsonrpc.EndpointCodecMap{\n\t\t\"sum\": jsonrpc.EndpointCodec{\n\t\t\tEndpoint: sumEndpoint,\n\t\t\tDecode:   decodeSumRequest,\n\t\t\tEncode:   encodeSumResponse,\n\t\t},\n\t}\n\n### Decoder\n\ttype DecodeRequestFunc func(context.Context, json.RawMessage) (request interface{}, err error)\n\nA `DecodeRequestFunc` is given the raw JSON from the `params` property of the Request object, _not_ the whole request object. It returns an object that will be the input to the Endpoint. For our purposes, the output should be a SumRequest, like this:\n\n\ttype SumRequest struct {\n\t\tA, B int\n\t}\n\nSo here's our decoder:\n\n\tfunc decodeSumRequest(ctx context.Context, msg json.RawMessage) (interface{}, error) {\n\t\tvar req SumRequest\n\t\terr := json.Unmarshal(msg, &req)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn req, nil\n\t}\n\nSo our `SumRequest` will now be passed to the endpoint. Once the endpoint has done its work, we hand over to the…\n\n### Encoder\nThe encoder takes the output of the endpoint, and builds the raw JSON message that will form the `result` field of a [Response Object](http://www.jsonrpc.org/specification#response_object). Our result is going to be a plain int. Here's our encoder:\n\n\tfunc encodeSumResponse(ctx context.Context, result interface{}) (json.RawMessage, error) {\n\t\tsum, ok := result.(int)\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"result is not an int\")\n\t\t}\n\t\tb, err := json.Marshal(sum)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn b, nil\n\t}\n\n### Server\nNow that we have an EndpointCodec with decoder, endpoint, and encoder, we can wire up the server:\n\n\thandler := jsonrpc.NewServer(jsonrpc.EndpointCodecMap{\n\t\t\"sum\": jsonrpc.EndpointCodec{\n\t\t\tEndpoint: sumEndpoint,\n\t\t\tDecode:   decodeSumRequest,\n\t\t\tEncode:   encodeSumResponse,\n\t\t},\n\t})\n\thttp.Handle(\"/rpc\", handler)\n\thttp.ListenAndServe(\":80\", nil)\n\nWith all of this done, our example request above should result in a response like this:\n\n\t{\n\t    \"jsonrpc\": \"2.0\",\n\t    \"result\": 4\n\t}\n"
  },
  {
    "path": "transport/http/jsonrpc/client.go",
    "content": "package jsonrpc\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"sync/atomic\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\thttptransport \"github.com/go-kit/kit/transport/http\"\n)\n\n// Client wraps a JSON RPC method and provides a method that implements endpoint.Endpoint.\ntype Client struct {\n\tclient httptransport.HTTPClient\n\n\t// JSON RPC endpoint URL\n\ttgt *url.URL\n\n\t// JSON RPC method name.\n\tmethod string\n\n\tenc            EncodeRequestFunc\n\tdec            DecodeResponseFunc\n\tbefore         []httptransport.RequestFunc\n\tafter          []httptransport.ClientResponseFunc\n\tfinalizer      httptransport.ClientFinalizerFunc\n\trequestID      RequestIDGenerator\n\tbufferedStream bool\n}\n\ntype clientRequest struct {\n\tJSONRPC string          `json:\"jsonrpc\"`\n\tMethod  string          `json:\"method\"`\n\tParams  json.RawMessage `json:\"params\"`\n\tID      interface{}     `json:\"id\"`\n}\n\n// NewClient constructs a usable Client for a single remote method.\nfunc NewClient(\n\ttgt *url.URL,\n\tmethod string,\n\toptions ...ClientOption,\n) *Client {\n\tc := &Client{\n\t\tclient:         http.DefaultClient,\n\t\tmethod:         method,\n\t\ttgt:            tgt,\n\t\tenc:            DefaultRequestEncoder,\n\t\tdec:            DefaultResponseDecoder,\n\t\tbefore:         []httptransport.RequestFunc{},\n\t\tafter:          []httptransport.ClientResponseFunc{},\n\t\trequestID:      NewAutoIncrementID(0),\n\t\tbufferedStream: false,\n\t}\n\tfor _, option := range options {\n\t\toption(c)\n\t}\n\treturn c\n}\n\n// DefaultRequestEncoder marshals the given request to JSON.\nfunc DefaultRequestEncoder(_ context.Context, req interface{}) (json.RawMessage, error) {\n\treturn json.Marshal(req)\n}\n\n// DefaultResponseDecoder unmarshals the result to interface{}, or returns an\n// error, if found.\nfunc DefaultResponseDecoder(_ context.Context, res Response) (interface{}, error) {\n\tif res.Error != nil {\n\t\treturn nil, *res.Error\n\t}\n\tvar result interface{}\n\terr := json.Unmarshal(res.Result, &result)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn result, nil\n}\n\n// ClientOption sets an optional parameter for clients.\ntype ClientOption func(*Client)\n\n// SetClient sets the underlying HTTP client used for requests.\n// By default, http.DefaultClient is used.\nfunc SetClient(client httptransport.HTTPClient) ClientOption {\n\treturn func(c *Client) { c.client = client }\n}\n\n// ClientBefore sets the RequestFuncs that are applied to the outgoing HTTP\n// request before it's invoked.\nfunc ClientBefore(before ...httptransport.RequestFunc) ClientOption {\n\treturn func(c *Client) { c.before = append(c.before, before...) }\n}\n\n// ClientAfter sets the ClientResponseFuncs applied to the server's HTTP\n// response prior to it being decoded. This is useful for obtaining anything\n// from the response and adding onto the context prior to decoding.\nfunc ClientAfter(after ...httptransport.ClientResponseFunc) ClientOption {\n\treturn func(c *Client) { c.after = append(c.after, after...) }\n}\n\n// ClientFinalizer is executed at the end of every HTTP request.\n// By default, no finalizer is registered.\nfunc ClientFinalizer(f httptransport.ClientFinalizerFunc) ClientOption {\n\treturn func(c *Client) { c.finalizer = f }\n}\n\n// ClientRequestEncoder sets the func used to encode the request params to JSON.\n// If not set, DefaultRequestEncoder is used.\nfunc ClientRequestEncoder(enc EncodeRequestFunc) ClientOption {\n\treturn func(c *Client) { c.enc = enc }\n}\n\n// ClientResponseDecoder sets the func used to decode the response params from\n// JSON. If not set, DefaultResponseDecoder is used.\nfunc ClientResponseDecoder(dec DecodeResponseFunc) ClientOption {\n\treturn func(c *Client) { c.dec = dec }\n}\n\n// RequestIDGenerator returns an ID for the request.\ntype RequestIDGenerator interface {\n\tGenerate() interface{}\n}\n\n// ClientRequestIDGenerator is executed before each request to generate an ID\n// for the request.\n// By default, AutoIncrementRequestID is used.\nfunc ClientRequestIDGenerator(g RequestIDGenerator) ClientOption {\n\treturn func(c *Client) { c.requestID = g }\n}\n\n// BufferedStream sets whether the Response.Body is left open, allowing it\n// to be read from later. Useful for transporting a file as a buffered stream.\nfunc BufferedStream(buffered bool) ClientOption {\n\treturn func(c *Client) { c.bufferedStream = buffered }\n}\n\n// Endpoint returns a usable endpoint that invokes the remote endpoint.\nfunc (c Client) Endpoint() endpoint.Endpoint {\n\treturn func(ctx context.Context, request interface{}) (interface{}, error) {\n\t\tctx, cancel := context.WithCancel(ctx)\n\t\tdefer cancel()\n\n\t\tvar (\n\t\t\tresp *http.Response\n\t\t\terr  error\n\t\t)\n\t\tif c.finalizer != nil {\n\t\t\tdefer func() {\n\t\t\t\tif resp != nil {\n\t\t\t\t\tctx = context.WithValue(ctx, httptransport.ContextKeyResponseHeaders, resp.Header)\n\t\t\t\t\tctx = context.WithValue(ctx, httptransport.ContextKeyResponseSize, resp.ContentLength)\n\t\t\t\t}\n\t\t\t\tc.finalizer(ctx, err)\n\t\t\t}()\n\t\t}\n\n\t\tctx = context.WithValue(ctx, ContextKeyRequestMethod, c.method)\n\n\t\tvar params json.RawMessage\n\t\tif params, err = c.enc(ctx, request); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\trpcReq := clientRequest{\n\t\t\tJSONRPC: Version,\n\t\t\tMethod:  c.method,\n\t\t\tParams:  params,\n\t\t\tID:      c.requestID.Generate(),\n\t\t}\n\n\t\treq, err := http.NewRequest(\"POST\", c.tgt.String(), nil)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treq.Header.Set(\"Content-Type\", \"application/json; charset=utf-8\")\n\t\tvar b bytes.Buffer\n\t\treq.Body = ioutil.NopCloser(&b)\n\t\terr = json.NewEncoder(&b).Encode(rpcReq)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tfor _, f := range c.before {\n\t\t\tctx = f(ctx, req)\n\t\t}\n\n\t\tresp, err = c.client.Do(req.WithContext(ctx))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif !c.bufferedStream {\n\t\t\tdefer resp.Body.Close()\n\t\t}\n\n\t\tfor _, f := range c.after {\n\t\t\tctx = f(ctx, resp)\n\t\t}\n\n\t\t// Decode the body into an object\n\t\tvar rpcRes Response\n\t\terr = json.NewDecoder(resp.Body).Decode(&rpcRes)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tresponse, err := c.dec(ctx, rpcRes)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn response, nil\n\t}\n}\n\n// ClientFinalizerFunc can be used to perform work at the end of a client HTTP\n// request, after the response is returned. The principal\n// intended use is for error logging. Additional response parameters are\n// provided in the context under keys with the ContextKeyResponse prefix.\n// Note: err may be nil. There maybe also no additional response parameters\n// depending on when an error occurs.\ntype ClientFinalizerFunc func(ctx context.Context, err error)\n\n// autoIncrementID is a RequestIDGenerator that generates\n// auto-incrementing integer IDs.\ntype autoIncrementID struct {\n\tv *uint64\n}\n\n// NewAutoIncrementID returns an auto-incrementing request ID generator,\n// initialised with the given value.\nfunc NewAutoIncrementID(init uint64) RequestIDGenerator {\n\t// Offset by one so that the first generated value = init.\n\tv := init - 1\n\treturn &autoIncrementID{v: &v}\n}\n\n// Generate satisfies RequestIDGenerator\nfunc (i *autoIncrementID) Generate() interface{} {\n\tid := atomic.AddUint64(i.v, 1)\n\treturn id\n}\n"
  },
  {
    "path": "transport/http/jsonrpc/client_test.go",
    "content": "package jsonrpc_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/go-kit/kit/transport/http/jsonrpc\"\n)\n\ntype TestResponse struct {\n\tBody   io.ReadCloser\n\tString string\n}\n\ntype testServerResponseOptions struct {\n\tBody   string\n\tStatus int\n}\n\nfunc httptestServer(t *testing.T) *httptest.Server {\n\treturn httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tdefer r.Body.Close()\n\n\t\tvar testReq jsonrpc.Request\n\t\tif err := json.NewDecoder(r.Body).Decode(&testReq); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tvar options testServerResponseOptions\n\t\tif err := json.Unmarshal(testReq.Params, &options); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif options.Status == 0 {\n\t\t\toptions.Status = http.StatusOK\n\t\t}\n\n\t\tw.WriteHeader(options.Status)\n\t\tw.Write([]byte(options.Body))\n\t}))\n}\n\nfunc TestBeforeAfterFuncs(t *testing.T) {\n\tt.Parallel()\n\n\tvar tests = []struct {\n\t\tname   string\n\t\tstatus int\n\t\tbody   string\n\t}{\n\t\t{\n\t\t\tname: \"empty body\",\n\t\t\tbody: \"\",\n\t\t},\n\t\t{\n\t\t\tname:   \"empty body 500\",\n\t\t\tbody:   \"\",\n\t\t\tstatus: 500,\n\t\t},\n\n\t\t{\n\t\t\tname: \"empty json body\",\n\t\t\tbody: \"{}\",\n\t\t},\n\t\t{\n\t\t\tname: \"error\",\n\t\t\tbody: `{\"jsonrpc\":\"2.0\",\"error\":{\"code\":32603,\"message\":\"Bad thing happened.\"}}`,\n\t\t},\n\t}\n\n\tserver := httptestServer(t)\n\tdefer server.Close()\n\n\ttestUrl, err := url.Parse(server.URL)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tbeforeCalled := false\n\t\t\tafterCalled := false\n\t\t\tfinalizerCalled := false\n\n\t\t\tsut := jsonrpc.NewClient(\n\t\t\t\ttestUrl,\n\t\t\t\t\"dummy\",\n\t\t\t\tjsonrpc.ClientBefore(func(ctx context.Context, req *http.Request) context.Context {\n\t\t\t\t\tbeforeCalled = true\n\t\t\t\t\treturn ctx\n\t\t\t\t}),\n\t\t\t\tjsonrpc.ClientAfter(func(ctx context.Context, resp *http.Response) context.Context {\n\t\t\t\t\tafterCalled = true\n\t\t\t\t\treturn ctx\n\t\t\t\t}),\n\t\t\t\tjsonrpc.ClientFinalizer(func(ctx context.Context, err error) {\n\t\t\t\t\tfinalizerCalled = true\n\t\t\t\t}),\n\t\t\t)\n\n\t\t\tsut.Endpoint()(context.TODO(), testServerResponseOptions{Body: tt.body, Status: tt.status})\n\t\t\tif !beforeCalled {\n\t\t\t\tt.Fatal(\"Expected client before func to be called. Wasn't.\")\n\t\t\t}\n\t\t\tif !afterCalled {\n\t\t\t\tt.Fatal(\"Expected client after func to be called. Wasn't.\")\n\t\t\t}\n\t\t\tif !finalizerCalled {\n\t\t\t\tt.Fatal(\"Expected client finalizer func to be called. Wasn't.\")\n\t\t\t}\n\n\t\t})\n\n\t}\n\n}\n\ntype staticIDGenerator int\n\nfunc (g staticIDGenerator) Generate() interface{} { return g }\n\nfunc TestClientHappyPath(t *testing.T) {\n\tt.Parallel()\n\n\tvar (\n\t\tafterCalledKey    = \"AC\"\n\t\tbeforeHeaderKey   = \"BF\"\n\t\tbeforeHeaderValue = \"beforeFuncWozEre\"\n\t\ttestbody          = `{\"jsonrpc\":\"2.0\", \"result\":5}`\n\t\trequestBody       []byte\n\t\tbeforeFunc        = func(ctx context.Context, r *http.Request) context.Context {\n\t\t\tr.Header.Add(beforeHeaderKey, beforeHeaderValue)\n\t\t\treturn ctx\n\t\t}\n\t\tencode = func(ctx context.Context, req interface{}) (json.RawMessage, error) {\n\t\t\treturn json.Marshal(req)\n\t\t}\n\t\tafterFunc = func(ctx context.Context, r *http.Response) context.Context {\n\t\t\treturn context.WithValue(ctx, afterCalledKey, true)\n\t\t}\n\t\tfinalizerCalled = false\n\t\tfin             = func(ctx context.Context, err error) {\n\t\t\tfinalizerCalled = true\n\t\t}\n\t\tdecode = func(ctx context.Context, res jsonrpc.Response) (interface{}, error) {\n\t\t\tif ac := ctx.Value(afterCalledKey); ac == nil {\n\t\t\t\tt.Fatal(\"after not called\")\n\t\t\t}\n\t\t\tvar result int\n\t\t\terr := json.Unmarshal(res.Result, &result)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn result, nil\n\t\t}\n\n\t\twantID = 666\n\t\tgen    = staticIDGenerator(wantID)\n\t)\n\n\tserver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tif r.Header.Get(beforeHeaderKey) != beforeHeaderValue {\n\t\t\tt.Fatal(\"Header not set by before func.\")\n\t\t}\n\n\t\tb, err := ioutil.ReadAll(r.Body)\n\t\tif err != nil && err != io.EOF {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\trequestBody = b\n\n\t\tw.WriteHeader(http.StatusOK)\n\t\tw.Write([]byte(testbody))\n\t}))\n\tdefer server.Close()\n\n\tsut := jsonrpc.NewClient(\n\t\tmustParse(server.URL),\n\t\t\"add\",\n\t\tjsonrpc.ClientRequestEncoder(encode),\n\t\tjsonrpc.ClientResponseDecoder(decode),\n\t\tjsonrpc.ClientBefore(beforeFunc),\n\t\tjsonrpc.ClientAfter(afterFunc),\n\t\tjsonrpc.ClientRequestIDGenerator(gen),\n\t\tjsonrpc.ClientFinalizer(fin),\n\t\tjsonrpc.SetClient(http.DefaultClient),\n\t\tjsonrpc.BufferedStream(false),\n\t)\n\n\ttype addRequest struct {\n\t\tA int\n\t\tB int\n\t}\n\n\tin := addRequest{2, 2}\n\n\tresult, err := sut.Endpoint()(context.Background(), in)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tri, ok := result.(int)\n\tif !ok {\n\t\tt.Fatalf(\"result is not int: (%T)%+v\", result, result)\n\t}\n\tif ri != 5 {\n\t\tt.Fatalf(\"want=5, got=%d\", ri)\n\t}\n\n\tvar requestAtServer jsonrpc.Request\n\terr = json.Unmarshal(requestBody, &requestAtServer)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif id, _ := requestAtServer.ID.Int(); id != wantID {\n\t\tt.Fatalf(\"Request ID at server: want=%d, got=%d\", wantID, id)\n\t}\n\tif requestAtServer.JSONRPC != jsonrpc.Version {\n\t\tt.Fatalf(\"JSON-RPC version at server: want=%s, got=%s\", jsonrpc.Version, requestAtServer.JSONRPC)\n\t}\n\n\tvar paramsAtServer addRequest\n\terr = json.Unmarshal(requestAtServer.Params, &paramsAtServer)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif paramsAtServer != in {\n\t\tt.Fatalf(\"want=%+v, got=%+v\", in, paramsAtServer)\n\t}\n\n\tif !finalizerCalled {\n\t\tt.Fatal(\"Expected finalizer to be called. Wasn't.\")\n\t}\n}\n\nfunc TestCanUseDefaults(t *testing.T) {\n\tt.Parallel()\n\n\tvar (\n\t\ttestbody    = `{\"jsonrpc\":\"2.0\", \"result\":\"boogaloo\"}`\n\t\trequestBody []byte\n\t)\n\n\tserver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tb, err := ioutil.ReadAll(r.Body)\n\t\tif err != nil && err != io.EOF {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\trequestBody = b\n\n\t\tw.WriteHeader(http.StatusOK)\n\t\tw.Write([]byte(testbody))\n\t}))\n\tdefer server.Close()\n\n\tsut := jsonrpc.NewClient(\n\t\tmustParse(server.URL),\n\t\t\"add\",\n\t)\n\n\ttype addRequest struct {\n\t\tA int\n\t\tB int\n\t}\n\n\tin := addRequest{2, 2}\n\n\tresult, err := sut.Endpoint()(context.Background(), in)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\trs, ok := result.(string)\n\tif !ok {\n\t\tt.Fatalf(\"result is not string: (%T)%+v\", result, result)\n\t}\n\tif rs != \"boogaloo\" {\n\t\tt.Fatalf(\"want=boogaloo, got=%s\", rs)\n\t}\n\n\tvar requestAtServer jsonrpc.Request\n\terr = json.Unmarshal(requestBody, &requestAtServer)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tvar paramsAtServer addRequest\n\terr = json.Unmarshal(requestAtServer.Params, &paramsAtServer)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif paramsAtServer != in {\n\t\tt.Fatalf(\"want=%+v, got=%+v\", in, paramsAtServer)\n\t}\n}\n\nfunc TestClientCanHandleJSONRPCError(t *testing.T) {\n\tt.Parallel()\n\n\tvar testbody = `{\n\t\t\"jsonrpc\": \"2.0\",\n\t\t\"error\": {\n\t\t\t\"code\": -32603,\n\t\t\t\"message\": \"Bad thing happened.\"\n\t\t}\n\t}`\n\tserver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(http.StatusOK)\n\t\tw.Write([]byte(testbody))\n\t}))\n\tdefer server.Close()\n\n\tsut := jsonrpc.NewClient(mustParse(server.URL), \"add\")\n\n\t_, err := sut.Endpoint()(context.Background(), 5)\n\tif err == nil {\n\t\tt.Fatal(\"Expected error, got none.\")\n\t}\n\n\t{\n\t\twant := \"Bad thing happened.\"\n\t\tgot := err.Error()\n\t\tif got != want {\n\t\t\tt.Fatalf(\"error message: want=%s, got=%s\", want, got)\n\t\t}\n\t}\n\n\ttype errorCoder interface {\n\t\tErrorCode() int\n\t}\n\tec, ok := err.(errorCoder)\n\tif !ok {\n\t\tt.Fatal(\"Error is not errorCoder\")\n\t}\n\n\t{\n\t\twant := -32603\n\t\tgot := ec.ErrorCode()\n\t\tif got != want {\n\t\t\tt.Fatalf(\"error code: want=%d, got=%d\", want, got)\n\t\t}\n\t}\n}\n\nfunc TestDefaultAutoIncrementer(t *testing.T) {\n\tt.Parallel()\n\n\tsut := jsonrpc.NewAutoIncrementID(0)\n\tvar want uint64\n\tfor ; want < 100; want++ {\n\t\tgot := sut.Generate()\n\t\tif got != want {\n\t\t\tt.Fatalf(\"want=%d, got=%d\", want, got)\n\t\t}\n\t}\n}\n\nfunc mustParse(s string) *url.URL {\n\tu, err := url.Parse(s)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn u\n}\n"
  },
  {
    "path": "transport/http/jsonrpc/doc.go",
    "content": "// Package jsonrpc provides a JSON RPC (v2.0) binding for endpoints.\n// See http://www.jsonrpc.org/specification\npackage jsonrpc\n"
  },
  {
    "path": "transport/http/jsonrpc/encode_decode.go",
    "content": "package jsonrpc\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\n\t\"context\"\n)\n\n// Server-Side Codec\n\n// EndpointCodec defines a server Endpoint and its associated codecs\ntype EndpointCodec struct {\n\tEndpoint endpoint.Endpoint\n\tDecode   DecodeRequestFunc\n\tEncode   EncodeResponseFunc\n}\n\n// EndpointCodecMap maps the Request.Method to the proper EndpointCodec\ntype EndpointCodecMap map[string]EndpointCodec\n\n// DecodeRequestFunc extracts a user-domain request object from raw JSON\n// It's designed to be used in JSON RPC servers, for server-side endpoints.\n// One straightforward DecodeRequestFunc could be something that unmarshals\n// JSON from the request body to the concrete request type.\ntype DecodeRequestFunc func(context.Context, json.RawMessage) (request interface{}, err error)\n\n// EncodeResponseFunc encodes the passed response object to a JSON RPC result.\n// It's designed to be used in HTTP servers, for server-side endpoints.\n// One straightforward EncodeResponseFunc could be something that JSON encodes\n// the object directly.\ntype EncodeResponseFunc func(context.Context, interface{}) (response json.RawMessage, err error)\n\n// Client-Side Codec\n\n// EncodeRequestFunc encodes the given request object to raw JSON.\n// It's designed to be used in JSON RPC clients, for client-side\n// endpoints. One straightforward EncodeResponseFunc could be something that\n// JSON encodes the object directly.\ntype EncodeRequestFunc func(context.Context, interface{}) (request json.RawMessage, err error)\n\n// DecodeResponseFunc extracts a user-domain response object from an JSON RPC\n// response object. It's designed to be used in JSON RPC clients, for\n// client-side endpoints. It is the responsibility of this function to decide\n// whether any error present in the JSON RPC response should be surfaced to the\n// client endpoint.\ntype DecodeResponseFunc func(context.Context, Response) (response interface{}, err error)\n"
  },
  {
    "path": "transport/http/jsonrpc/error.go",
    "content": "package jsonrpc\n\n// Error defines a JSON RPC error that can be returned\n// in a Response from the spec\n// http://www.jsonrpc.org/specification#error_object\ntype Error struct {\n\tCode    int         `json:\"code\"`\n\tMessage string      `json:\"message\"`\n\tData    interface{} `json:\"data,omitempty\"`\n}\n\n// Error implements error.\nfunc (e Error) Error() string {\n\tif e.Message != \"\" {\n\t\treturn e.Message\n\t}\n\treturn errorMessage[e.Code]\n}\n\n// ErrorCode returns the JSON RPC error code associated with the error.\nfunc (e Error) ErrorCode() int {\n\treturn e.Code\n}\n\nconst (\n\t// ParseError defines invalid JSON was received by the server.\n\t// An error occurred on the server while parsing the JSON text.\n\tParseError int = -32700\n\n\t// InvalidRequestError defines the JSON sent is not a valid Request object.\n\tInvalidRequestError int = -32600\n\n\t// MethodNotFoundError defines the method does not exist / is not available.\n\tMethodNotFoundError int = -32601\n\n\t// InvalidParamsError defines invalid method parameter(s).\n\tInvalidParamsError int = -32602\n\n\t// InternalError defines a server error\n\tInternalError int = -32603\n)\n\nvar errorMessage = map[int]string{\n\tParseError:          \"An error occurred on the server while parsing the JSON text.\",\n\tInvalidRequestError: \"The JSON sent is not a valid Request object.\",\n\tMethodNotFoundError: \"The method does not exist / is not available.\",\n\tInvalidParamsError:  \"Invalid method parameter(s).\",\n\tInternalError:       \"Internal JSON-RPC error.\",\n}\n\n// ErrorMessage returns a message for the JSON RPC error code. It returns the empty\n// string if the code is unknown.\nfunc ErrorMessage(code int) string {\n\treturn errorMessage[code]\n}\n\ntype parseError string\n\nfunc (e parseError) Error() string {\n\treturn string(e)\n}\nfunc (e parseError) ErrorCode() int {\n\treturn ParseError\n}\n\ntype invalidRequestError string\n\nfunc (e invalidRequestError) Error() string {\n\treturn string(e)\n}\nfunc (e invalidRequestError) ErrorCode() int {\n\treturn InvalidRequestError\n}\n\ntype methodNotFoundError string\n\nfunc (e methodNotFoundError) Error() string {\n\treturn string(e)\n}\nfunc (e methodNotFoundError) ErrorCode() int {\n\treturn MethodNotFoundError\n}\n\ntype invalidParamsError string\n\nfunc (e invalidParamsError) Error() string {\n\treturn string(e)\n}\nfunc (e invalidParamsError) ErrorCode() int {\n\treturn InvalidParamsError\n}\n\ntype internalError string\n\nfunc (e internalError) Error() string {\n\treturn string(e)\n}\nfunc (e internalError) ErrorCode() int {\n\treturn InternalError\n}\n"
  },
  {
    "path": "transport/http/jsonrpc/error_test.go",
    "content": "package jsonrpc\n\nimport \"testing\"\n\nfunc TestError(t *testing.T) {\n\twantCode := ParseError\n\tsut := Error{\n\t\tCode: wantCode,\n\t}\n\n\tgotCode := sut.ErrorCode()\n\tif gotCode != wantCode {\n\t\tt.Fatalf(\"want=%d, got=%d\", gotCode, wantCode)\n\t}\n\n\tif sut.Error() == \"\" {\n\t\tt.Fatal(\"Empty error string.\")\n\t}\n\n\twant := \"override\"\n\tsut.Message = want\n\tgot := sut.Error()\n\tif sut.Error() != want {\n\t\tt.Fatalf(\"overridden error message: want=%s, got=%s\", want, got)\n\t}\n\n}\nfunc TestErrorsSatisfyError(t *testing.T) {\n\terrs := []interface{}{\n\t\tparseError(\"parseError\"),\n\t\tinvalidRequestError(\"invalidRequestError\"),\n\t\tmethodNotFoundError(\"methodNotFoundError\"),\n\t\tinvalidParamsError(\"invalidParamsError\"),\n\t\tinternalError(\"internalError\"),\n\t}\n\tfor _, e := range errs {\n\t\terr, ok := e.(error)\n\t\tif !ok {\n\t\t\tt.Fatalf(\"Couldn't assert %s as error.\", e)\n\t\t}\n\t\terrString := err.Error()\n\t\tif errString == \"\" {\n\t\t\tt.Fatal(\"Empty error string\")\n\t\t}\n\n\t\tec, ok := e.(ErrorCoder)\n\t\tif !ok {\n\t\t\tt.Fatalf(\"Couldn't assert %s as ErrorCoder.\", e)\n\t\t}\n\t\tif ErrorMessage(ec.ErrorCode()) == \"\" {\n\t\t\tt.Fatalf(\"Error type %s returned code of %d, which does not map to error string\", e, ec.ErrorCode())\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "transport/http/jsonrpc/request_response_types.go",
    "content": "package jsonrpc\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n)\n\n// Request defines a JSON RPC request from the spec\n// http://www.jsonrpc.org/specification#request_object\ntype Request struct {\n\tJSONRPC string          `json:\"jsonrpc\"`\n\tMethod  string          `json:\"method\"`\n\tParams  json.RawMessage `json:\"params\"`\n\tID      *RequestID      `json:\"id\"`\n}\n\n// RequestID defines a request ID that can be string, number, or null.\n// An identifier established by the Client that MUST contain a String,\n// Number, or NULL value if included.\n// If it is not included it is assumed to be a notification.\n// The value SHOULD normally not be Null and\n// Numbers SHOULD NOT contain fractional parts.\ntype RequestID struct {\n\tintValue    int\n\tintError    error\n\tfloatValue  float32\n\tfloatError  error\n\tstringValue string\n\tstringError error\n}\n\n// RequestFunc may take information from decoded json body and place in\n// request context. In Servers, RequestFuncs are executed after json is parsed\n// but prior to invoking the codec\ntype RequestFunc func(context.Context, *http.Request, Request) context.Context\n\n// UnmarshalJSON satisfies json.Unmarshaler\nfunc (id *RequestID) UnmarshalJSON(b []byte) error {\n\tid.intError = json.Unmarshal(b, &id.intValue)\n\tid.floatError = json.Unmarshal(b, &id.floatValue)\n\tid.stringError = json.Unmarshal(b, &id.stringValue)\n\n\treturn nil\n}\n\nfunc (id *RequestID) MarshalJSON() ([]byte, error) {\n\tif id.intError == nil {\n\t\treturn json.Marshal(id.intValue)\n\t} else if id.floatError == nil {\n\t\treturn json.Marshal(id.floatValue)\n\t} else {\n\t\treturn json.Marshal(id.stringValue)\n\t}\n}\n\n// Int returns the ID as an integer value.\n// An error is returned if the ID can't be treated as an int.\nfunc (id *RequestID) Int() (int, error) {\n\treturn id.intValue, id.intError\n}\n\n// Float32 returns the ID as a float value.\n// An error is returned if the ID can't be treated as an float.\nfunc (id *RequestID) Float32() (float32, error) {\n\treturn id.floatValue, id.floatError\n}\n\n// String returns the ID as a string value.\n// An error is returned if the ID can't be treated as an string.\nfunc (id *RequestID) String() (string, error) {\n\treturn id.stringValue, id.stringError\n}\n\n// Response defines a JSON RPC response from the spec\n// http://www.jsonrpc.org/specification#response_object\ntype Response struct {\n\tJSONRPC string          `json:\"jsonrpc\"`\n\tResult  json.RawMessage `json:\"result,omitempty\"`\n\tError   *Error          `json:\"error,omitempty\"`\n\tID      *RequestID      `json:\"id\"`\n}\n\nconst (\n\t// Version defines the version of the JSON RPC implementation\n\tVersion string = \"2.0\"\n\n\t// ContentType defines the content type to be served.\n\tContentType string = \"application/json; charset=utf-8\"\n)\n\ntype contextKey int\n\nconst (\n\tContextKeyRequestMethod contextKey = iota\n)\n"
  },
  {
    "path": "transport/http/jsonrpc/request_response_types_test.go",
    "content": "package jsonrpc_test\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/go-kit/kit/transport/http/jsonrpc\"\n)\n\nfunc TestCanUnMarshalID(t *testing.T) {\n\tcases := []struct {\n\t\tJSON     string\n\t\texpType  string\n\t\texpValue interface{}\n\t}{\n\t\t{`12345`, \"int\", 12345},\n\t\t{`12345.6`, \"float\", 12345.6},\n\t\t{`\"stringaling\"`, \"string\", \"stringaling\"},\n\t}\n\n\tfor _, c := range cases {\n\t\tr := jsonrpc.Request{}\n\t\tJSON := fmt.Sprintf(`{\"id\":%s}`, c.JSON)\n\n\t\tvar foo interface{}\n\t\t_ = json.Unmarshal([]byte(JSON), &foo)\n\n\t\terr := json.Unmarshal([]byte(JSON), &r)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Unexpected error unmarshaling JSON into request: %s\\n\", err)\n\t\t}\n\t\tid := r.ID\n\n\t\tswitch c.expType {\n\t\tcase \"int\":\n\t\t\twant := c.expValue.(int)\n\t\t\tgot, err := id.Int()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tif got != want {\n\t\t\t\tt.Fatalf(\"'%s' Int(): want %d, got %d.\", c.JSON, want, got)\n\t\t\t}\n\n\t\t\t// Allow an int ID to be interpreted as a float.\n\t\t\twantf := float32(c.expValue.(int))\n\t\t\tgotf, err := id.Float32()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tif gotf != wantf {\n\t\t\t\tt.Fatalf(\"'%s' Int value as Float32(): want %f, got %f.\", c.JSON, wantf, gotf)\n\t\t\t}\n\n\t\t\t_, err = id.String()\n\t\t\tif err == nil {\n\t\t\t\tt.Fatal(\"Expected String() to error for int value. Didn't.\")\n\t\t\t}\n\t\tcase \"string\":\n\t\t\twant := c.expValue.(string)\n\t\t\tgot, err := id.String()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tif got != want {\n\t\t\t\tt.Fatalf(\"'%s' String(): want %s, got %s.\", c.JSON, want, got)\n\t\t\t}\n\n\t\t\t_, err = id.Int()\n\t\t\tif err == nil {\n\t\t\t\tt.Fatal(\"Expected Int() to error for string value. Didn't.\")\n\t\t\t}\n\t\t\t_, err = id.Float32()\n\t\t\tif err == nil {\n\t\t\t\tt.Fatal(\"Expected Float32() to error for string value. Didn't.\")\n\t\t\t}\n\t\tcase \"float32\":\n\t\t\twant := c.expValue.(float32)\n\t\t\tgot, err := id.Float32()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tif got != want {\n\t\t\t\tt.Fatalf(\"'%s' Float32(): want %f, got %f.\", c.JSON, want, got)\n\t\t\t}\n\n\t\t\t_, err = id.String()\n\t\t\tif err == nil {\n\t\t\t\tt.Fatal(\"Expected String() to error for float value. Didn't.\")\n\t\t\t}\n\t\t\t_, err = id.Int()\n\t\t\tif err == nil {\n\t\t\t\tt.Fatal(\"Expected Int() to error for float value. Didn't.\")\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestCanUnmarshalNullID(t *testing.T) {\n\tr := jsonrpc.Request{}\n\tJSON := `{\"id\":null}`\n\terr := json.Unmarshal([]byte(JSON), &r)\n\tif err != nil {\n\t\tt.Fatalf(\"Unexpected error unmarshaling JSON into request: %s\\n\", err)\n\t}\n\n\tif r.ID != nil {\n\t\tt.Fatalf(\"Expected ID to be nil, got %+v.\\n\", r.ID)\n\t}\n}\n\nfunc TestCanMarshalID(t *testing.T) {\n\tcases := []struct {\n\t\tJSON     string\n\t\texpType  string\n\t\texpValue interface{}\n\t}{\n\t\t{`12345`, \"int\", 12345},\n\t\t{`12345.6`, \"float\", 12345.6},\n\t\t{`\"stringaling\"`, \"string\", \"stringaling\"},\n\t\t{`null`, \"null\", nil},\n\t}\n\n\tfor _, c := range cases {\n\t\treq := jsonrpc.Request{}\n\t\tJSON := fmt.Sprintf(`{\"jsonrpc\":\"2.0\",\"id\":%s}`, c.JSON)\n\t\tjson.Unmarshal([]byte(JSON), &req)\n\t\tresp := jsonrpc.Response{ID: req.ID, JSONRPC: req.JSONRPC}\n\n\t\twant := JSON\n\t\tbol, _ := json.Marshal(resp)\n\t\tgot := string(bol)\n\t\tif got != want {\n\t\t\tt.Fatalf(\"'%s': want %s, got %s.\", c.expType, want, got)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "transport/http/jsonrpc/server.go",
    "content": "package jsonrpc\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\thttptransport \"github.com/go-kit/kit/transport/http\"\n\t\"github.com/go-kit/log\"\n)\n\ntype requestIDKeyType struct{}\n\nvar requestIDKey requestIDKeyType\n\n// Server wraps an endpoint and implements http.Handler.\ntype Server struct {\n\tecm          EndpointCodecMap\n\tbefore       []httptransport.RequestFunc\n\tbeforeCodec  []RequestFunc\n\tafter        []httptransport.ServerResponseFunc\n\terrorEncoder httptransport.ErrorEncoder\n\tfinalizer    httptransport.ServerFinalizerFunc\n\tlogger       log.Logger\n}\n\n// NewServer constructs a new server, which implements http.Server.\nfunc NewServer(\n\tecm EndpointCodecMap,\n\toptions ...ServerOption,\n) *Server {\n\ts := &Server{\n\t\tecm:          ecm,\n\t\terrorEncoder: DefaultErrorEncoder,\n\t\tlogger:       log.NewNopLogger(),\n\t}\n\tfor _, option := range options {\n\t\toption(s)\n\t}\n\treturn s\n}\n\n// ServerOption sets an optional parameter for servers.\ntype ServerOption func(*Server)\n\n// ServerBefore functions are executed on the HTTP request object before the\n// request is decoded.\nfunc ServerBefore(before ...httptransport.RequestFunc) ServerOption {\n\treturn func(s *Server) { s.before = append(s.before, before...) }\n}\n\n// ServerBeforeCodec functions are executed after the JSON request body has been\n// decoded, but before the method's decoder is called. This provides an opportunity\n// for middleware to inspect the contents of the rpc request before being passed\n// to the codec.\nfunc ServerBeforeCodec(beforeCodec ...RequestFunc) ServerOption {\n\treturn func(s *Server) { s.beforeCodec = append(s.beforeCodec, beforeCodec...) }\n}\n\n// ServerAfter functions are executed on the HTTP response writer after the\n// endpoint is invoked, but before anything is written to the client.\nfunc ServerAfter(after ...httptransport.ServerResponseFunc) ServerOption {\n\treturn func(s *Server) { s.after = append(s.after, after...) }\n}\n\n// ServerErrorEncoder is used to encode errors to the http.ResponseWriter\n// whenever they're encountered in the processing of a request. Clients can\n// use this to provide custom error formatting and response codes. By default,\n// errors will be written with the DefaultErrorEncoder.\nfunc ServerErrorEncoder(ee httptransport.ErrorEncoder) ServerOption {\n\treturn func(s *Server) { s.errorEncoder = ee }\n}\n\n// ServerErrorLogger is used to log non-terminal errors. By default, no errors\n// are logged. This is intended as a diagnostic measure. Finer-grained control\n// of error handling, including logging in more detail, should be performed in a\n// custom ServerErrorEncoder or ServerFinalizer, both of which have access to\n// the context.\nfunc ServerErrorLogger(logger log.Logger) ServerOption {\n\treturn func(s *Server) { s.logger = logger }\n}\n\n// ServerFinalizer is executed at the end of every HTTP request.\n// By default, no finalizer is registered.\nfunc ServerFinalizer(f httptransport.ServerFinalizerFunc) ServerOption {\n\treturn func(s *Server) { s.finalizer = f }\n}\n\n// ServeHTTP implements http.Handler.\nfunc (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tif r.Method != http.MethodPost {\n\t\tw.Header().Set(\"Content-Type\", \"text/plain; charset=utf-8\")\n\t\tw.WriteHeader(http.StatusMethodNotAllowed)\n\t\t_, _ = io.WriteString(w, \"405 must POST\\n\")\n\t\treturn\n\t}\n\tctx := r.Context()\n\n\tif s.finalizer != nil {\n\t\tiw := &interceptingWriter{w, http.StatusOK}\n\t\tdefer func() { s.finalizer(ctx, iw.code, r) }()\n\t\tw = iw\n\t}\n\n\tfor _, f := range s.before {\n\t\tctx = f(ctx, r)\n\t}\n\n\t// Decode the body into an  object\n\tvar req Request\n\terr := json.NewDecoder(r.Body).Decode(&req)\n\tif err != nil {\n\t\trpcerr := parseError(\"JSON could not be decoded: \" + err.Error())\n\t\ts.logger.Log(\"err\", rpcerr)\n\t\ts.errorEncoder(ctx, rpcerr, w)\n\t\treturn\n\t}\n\n\tctx = context.WithValue(ctx, requestIDKey, req.ID)\n\tctx = context.WithValue(ctx, ContextKeyRequestMethod, req.Method)\n\n\tfor _, f := range s.beforeCodec {\n\t\tctx = f(ctx, r, req)\n\t}\n\n\t// Get the endpoint and codecs from the map using the method\n\t// defined in the JSON  object\n\tecm, ok := s.ecm[req.Method]\n\tif !ok {\n\t\terr := methodNotFoundError(fmt.Sprintf(\"Method %s was not found.\", req.Method))\n\t\ts.logger.Log(\"err\", err)\n\t\ts.errorEncoder(ctx, err, w)\n\t\treturn\n\t}\n\n\t// Decode the JSON \"params\"\n\treqParams, err := ecm.Decode(ctx, req.Params)\n\tif err != nil {\n\t\ts.logger.Log(\"err\", err)\n\t\ts.errorEncoder(ctx, err, w)\n\t\treturn\n\t}\n\n\t// Call the Endpoint with the params\n\tresponse, err := ecm.Endpoint(ctx, reqParams)\n\tif err != nil {\n\t\ts.logger.Log(\"err\", err)\n\t\ts.errorEncoder(ctx, err, w)\n\t\treturn\n\t}\n\n\tfor _, f := range s.after {\n\t\tctx = f(ctx, w)\n\t}\n\n\tres := Response{\n\t\tID:      req.ID,\n\t\tJSONRPC: Version,\n\t}\n\n\t// Encode the response from the Endpoint\n\tresParams, err := ecm.Encode(ctx, response)\n\tif err != nil {\n\t\ts.logger.Log(\"err\", err)\n\t\ts.errorEncoder(ctx, err, w)\n\t\treturn\n\t}\n\n\tres.Result = resParams\n\n\tw.Header().Set(\"Content-Type\", ContentType)\n\t_ = json.NewEncoder(w).Encode(res)\n}\n\n// DefaultErrorEncoder writes the error to the ResponseWriter,\n// as a json-rpc error response, with an InternalError status code.\n// The Error() string of the error will be used as the response error message.\n// If the error implements ErrorCoder, the provided code will be set on the\n// response error.\n// If the error implements Headerer, the given headers will be set.\nfunc DefaultErrorEncoder(ctx context.Context, err error, w http.ResponseWriter) {\n\tw.Header().Set(\"Content-Type\", ContentType)\n\tif headerer, ok := err.(httptransport.Headerer); ok {\n\t\tfor k := range headerer.Headers() {\n\t\t\tw.Header().Set(k, headerer.Headers().Get(k))\n\t\t}\n\t}\n\n\te := Error{\n\t\tCode:    InternalError,\n\t\tMessage: err.Error(),\n\t}\n\tif sc, ok := err.(ErrorCoder); ok {\n\t\te.Code = sc.ErrorCode()\n\t}\n\n\tw.WriteHeader(http.StatusOK)\n\n\tvar requestID *RequestID\n\tif v := ctx.Value(requestIDKey); v != nil {\n\t\trequestID = v.(*RequestID)\n\t}\n\t_ = json.NewEncoder(w).Encode(Response{\n\t\tID:      requestID,\n\t\tJSONRPC: Version,\n\t\tError:   &e,\n\t})\n}\n\n// ErrorCoder is checked by DefaultErrorEncoder. If an error value implements\n// ErrorCoder, the integer result of ErrorCode() will be used as the JSONRPC\n// error code when encoding the error.\n//\n// By default, InternalError (-32603) is used.\ntype ErrorCoder interface {\n\tErrorCode() int\n}\n\n// interceptingWriter intercepts calls to WriteHeader, so that a finalizer\n// can be given the correct status code.\ntype interceptingWriter struct {\n\thttp.ResponseWriter\n\tcode int\n}\n\n// WriteHeader may not be explicitly called, so care must be taken to\n// initialize w.code to its default value of http.StatusOK.\nfunc (w *interceptingWriter) WriteHeader(code int) {\n\tw.code = code\n\tw.ResponseWriter.WriteHeader(code)\n}\n"
  },
  {
    "path": "transport/http/jsonrpc/server_test.go",
    "content": "package jsonrpc_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/transport/http/jsonrpc\"\n)\n\nfunc addBody() io.Reader {\n\treturn body(`{\"jsonrpc\": \"2.0\", \"method\": \"add\", \"params\": [3, 2], \"id\": 1}`)\n}\n\nfunc body(in string) io.Reader {\n\treturn strings.NewReader(in)\n}\n\nfunc unmarshalResponse(body []byte) (resp jsonrpc.Response, err error) {\n\terr = json.Unmarshal(body, &resp)\n\treturn\n}\n\nfunc expectErrorCode(t *testing.T, want int, body []byte) {\n\tt.Helper()\n\n\tr, err := unmarshalResponse(body)\n\tif err != nil {\n\t\tt.Fatalf(\"Can't decode response: %v (%s)\", err, body)\n\t}\n\tif r.Error == nil {\n\t\tt.Fatalf(\"Expected error on response. Got none: %s\", body)\n\t}\n\tif have := r.Error.Code; want != have {\n\t\tt.Fatalf(\"Unexpected error code. Want %d, have %d: %s\", want, have, body)\n\t}\n}\n\nfunc expectValidRequestID(t *testing.T, want int, body []byte) {\n\tt.Helper()\n\n\tr, err := unmarshalResponse(body)\n\tif err != nil {\n\t\tt.Fatalf(\"Can't decode response: %v (%s)\", err, body)\n\t}\n\thave, err := r.ID.Int()\n\tif err != nil {\n\t\tt.Fatalf(\"Can't get requestID in response. err=%s, body=%s\", err, body)\n\t}\n\tif want != have {\n\t\tt.Fatalf(\"Request ID: want %d, have %d (%s)\", want, have, body)\n\t}\n}\n\nfunc expectNilRequestID(t *testing.T, body []byte) {\n\tt.Helper()\n\n\tr, err := unmarshalResponse(body)\n\tif err != nil {\n\t\tt.Fatalf(\"Can't decode response: %v (%s)\", err, body)\n\t}\n\tif r.ID != nil {\n\t\tt.Fatalf(\"Request ID: want nil, have %v\", r.ID)\n\t}\n}\n\nfunc nopDecoder(context.Context, json.RawMessage) (interface{}, error) { return struct{}{}, nil }\nfunc nopEncoder(context.Context, interface{}) (json.RawMessage, error) { return []byte(\"[]\"), nil }\n\ntype mockLogger struct {\n\tCalled   bool\n\tLastArgs []interface{}\n}\n\nfunc (l *mockLogger) Log(keyvals ...interface{}) error {\n\tl.Called = true\n\tl.LastArgs = append(l.LastArgs, keyvals)\n\treturn nil\n}\n\nfunc TestServerBadDecode(t *testing.T) {\n\tecm := jsonrpc.EndpointCodecMap{\n\t\t\"add\": jsonrpc.EndpointCodec{\n\t\t\tEndpoint: endpoint.Nop,\n\t\t\tDecode:   func(context.Context, json.RawMessage) (interface{}, error) { return struct{}{}, errors.New(\"oof\") },\n\t\t\tEncode:   nopEncoder,\n\t\t},\n\t}\n\tlogger := mockLogger{}\n\thandler := jsonrpc.NewServer(ecm, jsonrpc.ServerErrorLogger(&logger))\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\tresp, _ := http.Post(server.URL, \"application/json\", addBody())\n\tbuf, _ := ioutil.ReadAll(resp.Body)\n\tif want, have := http.StatusOK, resp.StatusCode; want != have {\n\t\tt.Errorf(\"want %d, have %d: %s\", want, have, buf)\n\t}\n\texpectErrorCode(t, jsonrpc.InternalError, buf)\n\tif !logger.Called {\n\t\tt.Fatal(\"Expected logger to be called with error. Wasn't.\")\n\t}\n}\n\nfunc TestServerBadEndpoint(t *testing.T) {\n\tecm := jsonrpc.EndpointCodecMap{\n\t\t\"add\": jsonrpc.EndpointCodec{\n\t\t\tEndpoint: func(context.Context, interface{}) (interface{}, error) { return struct{}{}, errors.New(\"oof\") },\n\t\t\tDecode:   nopDecoder,\n\t\t\tEncode:   nopEncoder,\n\t\t},\n\t}\n\thandler := jsonrpc.NewServer(ecm)\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\tresp, _ := http.Post(server.URL, \"application/json\", addBody())\n\tif want, have := http.StatusOK, resp.StatusCode; want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\tbuf, _ := ioutil.ReadAll(resp.Body)\n\texpectErrorCode(t, jsonrpc.InternalError, buf)\n\texpectValidRequestID(t, 1, buf)\n}\n\nfunc TestServerBadEncode(t *testing.T) {\n\tecm := jsonrpc.EndpointCodecMap{\n\t\t\"add\": jsonrpc.EndpointCodec{\n\t\t\tEndpoint: endpoint.Nop,\n\t\t\tDecode:   nopDecoder,\n\t\t\tEncode:   func(context.Context, interface{}) (json.RawMessage, error) { return []byte{}, errors.New(\"oof\") },\n\t\t},\n\t}\n\thandler := jsonrpc.NewServer(ecm)\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\tresp, _ := http.Post(server.URL, \"application/json\", addBody())\n\tif want, have := http.StatusOK, resp.StatusCode; want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\tbuf, _ := ioutil.ReadAll(resp.Body)\n\texpectErrorCode(t, jsonrpc.InternalError, buf)\n\texpectValidRequestID(t, 1, buf)\n}\n\nfunc TestServerErrorEncoder(t *testing.T) {\n\terrTeapot := errors.New(\"teapot\")\n\tcode := func(err error) int {\n\t\tif errors.Is(err, errTeapot) {\n\t\t\treturn http.StatusTeapot\n\t\t}\n\t\treturn http.StatusInternalServerError\n\t}\n\tecm := jsonrpc.EndpointCodecMap{\n\t\t\"add\": jsonrpc.EndpointCodec{\n\t\t\tEndpoint: func(context.Context, interface{}) (interface{}, error) { return struct{}{}, errTeapot },\n\t\t\tDecode:   nopDecoder,\n\t\t\tEncode:   nopEncoder,\n\t\t},\n\t}\n\thandler := jsonrpc.NewServer(\n\t\tecm,\n\t\tjsonrpc.ServerErrorEncoder(func(_ context.Context, err error, w http.ResponseWriter) { w.WriteHeader(code(err)) }),\n\t)\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\tresp, _ := http.Post(server.URL, \"application/json\", addBody())\n\tif want, have := http.StatusTeapot, resp.StatusCode; want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\nfunc TestCanRejectNonPostRequest(t *testing.T) {\n\tecm := jsonrpc.EndpointCodecMap{}\n\thandler := jsonrpc.NewServer(ecm)\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\tresp, _ := http.Get(server.URL)\n\tif want, have := http.StatusMethodNotAllowed, resp.StatusCode; want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\nfunc TestCanRejectInvalidJSON(t *testing.T) {\n\tecm := jsonrpc.EndpointCodecMap{}\n\thandler := jsonrpc.NewServer(ecm)\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\tresp, _ := http.Post(server.URL, \"application/json\", body(\"clearlynotjson\"))\n\tif want, have := http.StatusOK, resp.StatusCode; want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\tbuf, _ := ioutil.ReadAll(resp.Body)\n\texpectErrorCode(t, jsonrpc.ParseError, buf)\n\texpectNilRequestID(t, buf)\n}\n\nfunc TestServerUnregisteredMethod(t *testing.T) {\n\tecm := jsonrpc.EndpointCodecMap{}\n\thandler := jsonrpc.NewServer(ecm)\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\tresp, _ := http.Post(server.URL, \"application/json\", addBody())\n\tif want, have := http.StatusOK, resp.StatusCode; want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\tbuf, _ := ioutil.ReadAll(resp.Body)\n\texpectErrorCode(t, jsonrpc.MethodNotFoundError, buf)\n}\n\nfunc TestServerHappyPath(t *testing.T) {\n\tstep, response := testServer(t)\n\tstep()\n\tresp := <-response\n\tdefer resp.Body.Close() // nolint\n\tbuf, _ := ioutil.ReadAll(resp.Body)\n\tif want, have := http.StatusOK, resp.StatusCode; want != have {\n\t\tt.Errorf(\"want %d, have %d (%s)\", want, have, buf)\n\t}\n\tr, err := unmarshalResponse(buf)\n\tif err != nil {\n\t\tt.Fatalf(\"Can't decode response. err=%s, body=%s\", err, buf)\n\t}\n\tif r.JSONRPC != jsonrpc.Version {\n\t\tt.Fatalf(\"JSONRPC Version: want=%s, got=%s\", jsonrpc.Version, r.JSONRPC)\n\t}\n\tif r.Error != nil {\n\t\tt.Fatalf(\"Unxpected error on response: %s\", buf)\n\t}\n}\n\nfunc TestMultipleServerBeforeCodec(t *testing.T) {\n\tvar done = make(chan struct{})\n\tecm := jsonrpc.EndpointCodecMap{\n\t\t\"add\": jsonrpc.EndpointCodec{\n\t\t\tEndpoint: endpoint.Nop,\n\t\t\tDecode:   nopDecoder,\n\t\t\tEncode:   nopEncoder,\n\t\t},\n\t}\n\thandler := jsonrpc.NewServer(\n\t\tecm,\n\t\tjsonrpc.ServerBeforeCodec(func(ctx context.Context, r *http.Request, req jsonrpc.Request) context.Context {\n\t\t\tctx = context.WithValue(ctx, \"one\", 1)\n\n\t\t\treturn ctx\n\t\t}),\n\t\tjsonrpc.ServerBeforeCodec(func(ctx context.Context, r *http.Request, req jsonrpc.Request) context.Context {\n\t\t\tif _, ok := ctx.Value(\"one\").(int); !ok {\n\t\t\t\tt.Error(\"Value was not set properly when multiple ServerBeforeCodecs are used\")\n\t\t\t}\n\n\t\t\tclose(done)\n\t\t\treturn ctx\n\t\t}),\n\t)\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\thttp.Post(server.URL, \"application/json\", addBody()) // nolint\n\n\tselect {\n\tcase <-done:\n\tcase <-time.After(time.Second):\n\t\tt.Fatal(\"timeout waiting for finalizer\")\n\t}\n}\n\nfunc TestMultipleServerBefore(t *testing.T) {\n\tvar done = make(chan struct{})\n\tecm := jsonrpc.EndpointCodecMap{\n\t\t\"add\": jsonrpc.EndpointCodec{\n\t\t\tEndpoint: endpoint.Nop,\n\t\t\tDecode:   nopDecoder,\n\t\t\tEncode:   nopEncoder,\n\t\t},\n\t}\n\thandler := jsonrpc.NewServer(\n\t\tecm,\n\t\tjsonrpc.ServerBefore(func(ctx context.Context, r *http.Request) context.Context {\n\t\t\tctx = context.WithValue(ctx, \"one\", 1)\n\n\t\t\treturn ctx\n\t\t}),\n\t\tjsonrpc.ServerBefore(func(ctx context.Context, r *http.Request) context.Context {\n\t\t\tif _, ok := ctx.Value(\"one\").(int); !ok {\n\t\t\t\tt.Error(\"Value was not set properly when multiple ServerBefores are used\")\n\t\t\t}\n\n\t\t\tclose(done)\n\t\t\treturn ctx\n\t\t}),\n\t)\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\thttp.Post(server.URL, \"application/json\", addBody()) // nolint\n\n\tselect {\n\tcase <-done:\n\tcase <-time.After(time.Second):\n\t\tt.Fatal(\"timeout waiting for finalizer\")\n\t}\n}\n\nfunc TestMultipleServerAfter(t *testing.T) {\n\tvar done = make(chan struct{})\n\tecm := jsonrpc.EndpointCodecMap{\n\t\t\"add\": jsonrpc.EndpointCodec{\n\t\t\tEndpoint: endpoint.Nop,\n\t\t\tDecode:   nopDecoder,\n\t\t\tEncode:   nopEncoder,\n\t\t},\n\t}\n\thandler := jsonrpc.NewServer(\n\t\tecm,\n\t\tjsonrpc.ServerAfter(func(ctx context.Context, w http.ResponseWriter) context.Context {\n\t\t\tctx = context.WithValue(ctx, \"one\", 1)\n\n\t\t\treturn ctx\n\t\t}),\n\t\tjsonrpc.ServerAfter(func(ctx context.Context, w http.ResponseWriter) context.Context {\n\t\t\tif _, ok := ctx.Value(\"one\").(int); !ok {\n\t\t\t\tt.Error(\"Value was not set properly when multiple ServerAfters are used\")\n\t\t\t}\n\n\t\t\tclose(done)\n\t\t\treturn ctx\n\t\t}),\n\t)\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\thttp.Post(server.URL, \"application/json\", addBody()) // nolint\n\n\tselect {\n\tcase <-done:\n\tcase <-time.After(time.Second):\n\t\tt.Fatal(\"timeout waiting for finalizer\")\n\t}\n}\n\nfunc TestCanFinalize(t *testing.T) {\n\tvar done = make(chan struct{})\n\tvar finalizerCalled bool\n\tecm := jsonrpc.EndpointCodecMap{\n\t\t\"add\": jsonrpc.EndpointCodec{\n\t\t\tEndpoint: endpoint.Nop,\n\t\t\tDecode:   nopDecoder,\n\t\t\tEncode:   nopEncoder,\n\t\t},\n\t}\n\thandler := jsonrpc.NewServer(\n\t\tecm,\n\t\tjsonrpc.ServerFinalizer(func(ctx context.Context, code int, req *http.Request) {\n\t\t\tfinalizerCalled = true\n\t\t\tclose(done)\n\t\t}),\n\t)\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\thttp.Post(server.URL, \"application/json\", addBody()) // nolint\n\n\tselect {\n\tcase <-done:\n\tcase <-time.After(time.Second):\n\t\tt.Fatal(\"timeout waiting for finalizer\")\n\t}\n\n\tif !finalizerCalled {\n\t\tt.Fatal(\"Finalizer was not called.\")\n\t}\n}\n\nfunc testServer(t *testing.T) (step func(), resp <-chan *http.Response) {\n\tvar (\n\t\tstepch   = make(chan bool)\n\t\tendpoint = func(ctx context.Context, request interface{}) (response interface{}, err error) {\n\t\t\t<-stepch\n\t\t\treturn struct{}{}, nil\n\t\t}\n\t\tresponse = make(chan *http.Response)\n\t\tecm      = jsonrpc.EndpointCodecMap{\n\t\t\t\"add\": jsonrpc.EndpointCodec{\n\t\t\t\tEndpoint: endpoint,\n\t\t\t\tDecode:   nopDecoder,\n\t\t\t\tEncode:   nopEncoder,\n\t\t\t},\n\t\t}\n\t\thandler = jsonrpc.NewServer(ecm)\n\t)\n\tgo func() {\n\t\tserver := httptest.NewServer(handler)\n\t\tdefer server.Close()\n\t\trb := strings.NewReader(`{\"jsonrpc\": \"2.0\", \"method\": \"add\", \"params\": [3, 2], \"id\": 1}`)\n\t\tresp, err := http.Post(server.URL, \"application/json\", rb)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\treturn\n\t\t}\n\t\tresponse <- resp\n\t}()\n\treturn func() { stepch <- true }, response\n}\n"
  },
  {
    "path": "transport/http/proto/client.go",
    "content": "package proto\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\n\thttptransport \"github.com/go-kit/kit/transport/http\"\n\t\"google.golang.org/protobuf/proto\"\n)\n\n// EncodeProtoRequest is an EncodeRequestFunc that serializes the request as Protobuf.\n// If the request implements Headerer, the provided headers will be applied\n// to the request. If the given request does not implement proto.Message, an error will\n// be returned.\nfunc EncodeProtoRequest(_ context.Context, r *http.Request, preq interface{}) error {\n\tr.Header.Set(\"Content-Type\", \"application/x-protobuf\")\n\tif headerer, ok := preq.(httptransport.Headerer); ok {\n\t\tfor k := range headerer.Headers() {\n\t\t\tr.Header.Set(k, headerer.Headers().Get(k))\n\t\t}\n\t}\n\treq, ok := preq.(proto.Message)\n\tif !ok {\n\t\treturn errors.New(\"response does not implement proto.Message\")\n\t}\n\n\tb, err := proto.Marshal(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tr.ContentLength = int64(len(b))\n\tr.Body = ioutil.NopCloser(bytes.NewReader(b))\n\treturn nil\n}\n"
  },
  {
    "path": "transport/http/proto/generate.go",
    "content": "package proto\n\n//go:generate protoc proto_test.proto --go_out=. --go_opt=Mproto_test.proto=github.com/go-kit/kit/transport/http/proto --go_opt=paths=source_relative\n//go:generate mv proto_test.pb.go proto_pb_test.go\n"
  },
  {
    "path": "transport/http/proto/proto_pb_test.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.26.0\n// \tprotoc        v3.16.0\n// source: proto_test.proto\n\npackage proto\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype Cat struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tAge   int32  `protobuf:\"varint,1,opt,name=Age,proto3\" json:\"Age,omitempty\"`\n\tBreed string `protobuf:\"bytes,2,opt,name=Breed,proto3\" json:\"Breed,omitempty\"`\n\tName  string `protobuf:\"bytes,3,opt,name=Name,proto3\" json:\"Name,omitempty\"`\n}\n\nfunc (x *Cat) Reset() {\n\t*x = Cat{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_proto_test_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Cat) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Cat) ProtoMessage() {}\n\nfunc (x *Cat) ProtoReflect() protoreflect.Message {\n\tmi := &file_proto_test_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Cat.ProtoReflect.Descriptor instead.\nfunc (*Cat) Descriptor() ([]byte, []int) {\n\treturn file_proto_test_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *Cat) GetAge() int32 {\n\tif x != nil {\n\t\treturn x.Age\n\t}\n\treturn 0\n}\n\nfunc (x *Cat) GetBreed() string {\n\tif x != nil {\n\t\treturn x.Breed\n\t}\n\treturn \"\"\n}\n\nfunc (x *Cat) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\nvar File_proto_test_proto protoreflect.FileDescriptor\n\nvar file_proto_test_proto_rawDesc = []byte{\n\t0x0a, 0x10, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x22, 0x41, 0x0a, 0x03, 0x43, 0x61, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x41, 0x67, 0x65,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x41, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x42,\n\t0x72, 0x65, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x42, 0x72, 0x65, 0x65,\n\t0x64, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,\n\t0x04, 0x4e, 0x61, 0x6d, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_proto_test_proto_rawDescOnce sync.Once\n\tfile_proto_test_proto_rawDescData = file_proto_test_proto_rawDesc\n)\n\nfunc file_proto_test_proto_rawDescGZIP() []byte {\n\tfile_proto_test_proto_rawDescOnce.Do(func() {\n\t\tfile_proto_test_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_test_proto_rawDescData)\n\t})\n\treturn file_proto_test_proto_rawDescData\n}\n\nvar file_proto_test_proto_msgTypes = make([]protoimpl.MessageInfo, 1)\nvar file_proto_test_proto_goTypes = []interface{}{\n\t(*Cat)(nil), // 0: Cat\n}\nvar file_proto_test_proto_depIdxs = []int32{\n\t0, // [0:0] is the sub-list for method output_type\n\t0, // [0:0] is the sub-list for method input_type\n\t0, // [0:0] is the sub-list for extension type_name\n\t0, // [0:0] is the sub-list for extension extendee\n\t0, // [0:0] is the sub-list for field type_name\n}\n\nfunc init() { file_proto_test_proto_init() }\nfunc file_proto_test_proto_init() {\n\tif File_proto_test_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_proto_test_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Cat); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_proto_test_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   1,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_proto_test_proto_goTypes,\n\t\tDependencyIndexes: file_proto_test_proto_depIdxs,\n\t\tMessageInfos:      file_proto_test_proto_msgTypes,\n\t}.Build()\n\tFile_proto_test_proto = out.File\n\tfile_proto_test_proto_rawDesc = nil\n\tfile_proto_test_proto_goTypes = nil\n\tfile_proto_test_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "transport/http/proto/proto_test.go",
    "content": "package proto\n\nimport (\n\t\"context\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"google.golang.org/protobuf/proto\"\n)\n\nfunc TestEncodeProtoRequest(t *testing.T) {\n\tcat := &Cat{Name: \"Ziggy\", Age: 13, Breed: \"Lumpy\"}\n\n\tr := httptest.NewRequest(http.MethodGet, \"/cat\", nil)\n\n\terr := EncodeProtoRequest(context.TODO(), r, cat)\n\tif err != nil {\n\t\tt.Errorf(\"expected no encoding errors but got: %s\", err)\n\t\treturn\n\t}\n\n\tconst xproto = \"application/x-protobuf\"\n\tif typ := r.Header.Get(\"Content-Type\"); typ != xproto {\n\t\tt.Errorf(\"expected content type of %q, got %q\", xproto, typ)\n\t\treturn\n\t}\n\n\tbod, err := ioutil.ReadAll(r.Body)\n\tif err != nil {\n\t\tt.Errorf(\"expected no read errors but got: %s\", err)\n\t\treturn\n\t}\n\tdefer r.Body.Close()\n\n\tvar got Cat\n\terr = proto.Unmarshal(bod, &got)\n\tif err != nil {\n\t\tt.Errorf(\"expected no proto errors but got: %s\", err)\n\t\treturn\n\t}\n\n\tif !proto.Equal(&got, cat) {\n\t\tt.Errorf(\"expected cats to be equal but got:\\n\\n%#v\\n\\nwant:\\n\\n%#v\", got, cat)\n\t\treturn\n\t}\n}\n\nfunc TestEncodeProtoResponse(t *testing.T) {\n\tcat := &Cat{Name: \"Ziggy\", Age: 13, Breed: \"Lumpy\"}\n\n\twr := httptest.NewRecorder()\n\n\terr := EncodeProtoResponse(context.TODO(), wr, cat)\n\tif err != nil {\n\t\tt.Errorf(\"expected no encoding errors but got: %s\", err)\n\t\treturn\n\t}\n\n\tw := wr.Result()\n\n\tconst xproto = \"application/x-protobuf\"\n\tif typ := w.Header.Get(\"Content-Type\"); typ != xproto {\n\t\tt.Errorf(\"expected content type of %q, got %q\", xproto, typ)\n\t\treturn\n\t}\n\n\tif w.StatusCode != http.StatusTeapot {\n\t\tt.Errorf(\"expected status code of %d, got %d\", http.StatusTeapot, w.StatusCode)\n\t\treturn\n\t}\n\n\tbod, err := ioutil.ReadAll(w.Body)\n\tif err != nil {\n\t\tt.Errorf(\"expected no read errors but got: %s\", err)\n\t\treturn\n\t}\n\tdefer w.Body.Close()\n\n\tvar got Cat\n\terr = proto.Unmarshal(bod, &got)\n\tif err != nil {\n\t\tt.Errorf(\"expected no proto errors but got: %s\", err)\n\t\treturn\n\t}\n\n\tif !proto.Equal(&got, cat) {\n\t\tt.Errorf(\"expected cats to be equal but got:\\n\\n%#v\\n\\nwant:\\n\\n%#v\", got, cat)\n\t\treturn\n\t}\n}\n\nfunc (c *Cat) StatusCode() int {\n\treturn http.StatusTeapot\n}\n"
  },
  {
    "path": "transport/http/proto/proto_test.proto",
    "content": "syntax = \"proto3\";\n\nmessage Cat {\n    int32 Age = 1;\n    string Breed = 2;\n    string Name = 3;\n}\n"
  },
  {
    "path": "transport/http/proto/server.go",
    "content": "package proto\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net/http\"\n\n\thttptransport \"github.com/go-kit/kit/transport/http\"\n\t\"google.golang.org/protobuf/proto\"\n)\n\n// EncodeProtoResponse is an EncodeResponseFunc that serializes the response as Protobuf.\n// Many Proto-over-HTTP services can use it as a sensible default. If the response\n// implements Headerer, the provided headers will be applied to the response. If the\n// response implements StatusCoder, the provided StatusCode will be used instead of 200.\nfunc EncodeProtoResponse(ctx context.Context, w http.ResponseWriter, pres interface{}) error {\n\tres, ok := pres.(proto.Message)\n\tif !ok {\n\t\treturn errors.New(\"response does not implement proto.Message\")\n\t}\n\tw.Header().Set(\"Content-Type\", \"application/x-protobuf\")\n\tif headerer, ok := w.(httptransport.Headerer); ok {\n\t\tfor k := range headerer.Headers() {\n\t\t\tw.Header().Set(k, headerer.Headers().Get(k))\n\t\t}\n\t}\n\tcode := http.StatusOK\n\tif sc, ok := pres.(httptransport.StatusCoder); ok {\n\t\tcode = sc.StatusCode()\n\t}\n\tw.WriteHeader(code)\n\tif code == http.StatusNoContent {\n\t\treturn nil\n\t}\n\tif res == nil {\n\t\treturn nil\n\t}\n\tb, err := proto.Marshal(res)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = w.Write(b)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "transport/http/request_response_funcs.go",
    "content": "package http\n\nimport (\n\t\"context\"\n\t\"net/http\"\n)\n\n// RequestFunc may take information from an HTTP request and put it into a\n// request context. In Servers, RequestFuncs are executed prior to invoking the\n// endpoint. In Clients, RequestFuncs are executed after creating the request\n// but prior to invoking the HTTP client.\ntype RequestFunc func(context.Context, *http.Request) context.Context\n\n// ServerResponseFunc may take information from a request context and use it to\n// manipulate a ResponseWriter. ServerResponseFuncs are only executed in\n// servers, after invoking the endpoint but prior to writing a response.\ntype ServerResponseFunc func(context.Context, http.ResponseWriter) context.Context\n\n// ClientResponseFunc may take information from an HTTP request and make the\n// response available for consumption. ClientResponseFuncs are only executed in\n// clients, after a request has been made, but prior to it being decoded.\ntype ClientResponseFunc func(context.Context, *http.Response) context.Context\n\n// SetContentType returns a ServerResponseFunc that sets the Content-Type header\n// to the provided value.\nfunc SetContentType(contentType string) ServerResponseFunc {\n\treturn SetResponseHeader(\"Content-Type\", contentType)\n}\n\n// SetResponseHeader returns a ServerResponseFunc that sets the given header.\nfunc SetResponseHeader(key, val string) ServerResponseFunc {\n\treturn func(ctx context.Context, w http.ResponseWriter) context.Context {\n\t\tw.Header().Set(key, val)\n\t\treturn ctx\n\t}\n}\n\n// SetRequestHeader returns a RequestFunc that sets the given header.\nfunc SetRequestHeader(key, val string) RequestFunc {\n\treturn func(ctx context.Context, r *http.Request) context.Context {\n\t\tr.Header.Set(key, val)\n\t\treturn ctx\n\t}\n}\n\n// PopulateRequestContext is a RequestFunc that populates several values into\n// the context from the HTTP request. Those values may be extracted using the\n// corresponding ContextKey type in this package.\nfunc PopulateRequestContext(ctx context.Context, r *http.Request) context.Context {\n\tfor k, v := range map[contextKey]string{\n\t\tContextKeyRequestMethod:          r.Method,\n\t\tContextKeyRequestURI:             r.RequestURI,\n\t\tContextKeyRequestPath:            r.URL.Path,\n\t\tContextKeyRequestProto:           r.Proto,\n\t\tContextKeyRequestHost:            r.Host,\n\t\tContextKeyRequestRemoteAddr:      r.RemoteAddr,\n\t\tContextKeyRequestXForwardedFor:   r.Header.Get(\"X-Forwarded-For\"),\n\t\tContextKeyRequestXForwardedProto: r.Header.Get(\"X-Forwarded-Proto\"),\n\t\tContextKeyRequestAuthorization:   r.Header.Get(\"Authorization\"),\n\t\tContextKeyRequestReferer:         r.Header.Get(\"Referer\"),\n\t\tContextKeyRequestUserAgent:       r.Header.Get(\"User-Agent\"),\n\t\tContextKeyRequestXRequestID:      r.Header.Get(\"X-Request-Id\"),\n\t\tContextKeyRequestAccept:          r.Header.Get(\"Accept\"),\n\t} {\n\t\tctx = context.WithValue(ctx, k, v)\n\t}\n\treturn ctx\n}\n\ntype contextKey int\n\nconst (\n\t// ContextKeyRequestMethod is populated in the context by\n\t// PopulateRequestContext. Its value is r.Method.\n\tContextKeyRequestMethod contextKey = iota\n\n\t// ContextKeyRequestURI is populated in the context by\n\t// PopulateRequestContext. Its value is r.RequestURI.\n\tContextKeyRequestURI\n\n\t// ContextKeyRequestPath is populated in the context by\n\t// PopulateRequestContext. Its value is r.URL.Path.\n\tContextKeyRequestPath\n\n\t// ContextKeyRequestProto is populated in the context by\n\t// PopulateRequestContext. Its value is r.Proto.\n\tContextKeyRequestProto\n\n\t// ContextKeyRequestHost is populated in the context by\n\t// PopulateRequestContext. Its value is r.Host.\n\tContextKeyRequestHost\n\n\t// ContextKeyRequestRemoteAddr is populated in the context by\n\t// PopulateRequestContext. Its value is r.RemoteAddr.\n\tContextKeyRequestRemoteAddr\n\n\t// ContextKeyRequestXForwardedFor is populated in the context by\n\t// PopulateRequestContext. Its value is r.Header.Get(\"X-Forwarded-For\").\n\tContextKeyRequestXForwardedFor\n\n\t// ContextKeyRequestXForwardedProto is populated in the context by\n\t// PopulateRequestContext. Its value is r.Header.Get(\"X-Forwarded-Proto\").\n\tContextKeyRequestXForwardedProto\n\n\t// ContextKeyRequestAuthorization is populated in the context by\n\t// PopulateRequestContext. Its value is r.Header.Get(\"Authorization\").\n\tContextKeyRequestAuthorization\n\n\t// ContextKeyRequestReferer is populated in the context by\n\t// PopulateRequestContext. Its value is r.Header.Get(\"Referer\").\n\tContextKeyRequestReferer\n\n\t// ContextKeyRequestUserAgent is populated in the context by\n\t// PopulateRequestContext. Its value is r.Header.Get(\"User-Agent\").\n\tContextKeyRequestUserAgent\n\n\t// ContextKeyRequestXRequestID is populated in the context by\n\t// PopulateRequestContext. Its value is r.Header.Get(\"X-Request-Id\").\n\tContextKeyRequestXRequestID\n\n\t// ContextKeyRequestAccept is populated in the context by\n\t// PopulateRequestContext. Its value is r.Header.Get(\"Accept\").\n\tContextKeyRequestAccept\n\n\t// ContextKeyResponseHeaders is populated in the context whenever a\n\t// ServerFinalizerFunc is specified. Its value is of type http.Header, and\n\t// is captured only once the entire response has been written.\n\tContextKeyResponseHeaders\n\n\t// ContextKeyResponseSize is populated in the context whenever a\n\t// ServerFinalizerFunc is specified. Its value is of type int64.\n\tContextKeyResponseSize\n)\n"
  },
  {
    "path": "transport/http/request_response_funcs_test.go",
    "content": "package http_test\n\nimport (\n\t\"context\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\thttptransport \"github.com/go-kit/kit/transport/http\"\n)\n\nfunc TestSetHeader(t *testing.T) {\n\tconst (\n\t\tkey = \"X-Foo\"\n\t\tval = \"12345\"\n\t)\n\tr := httptest.NewRecorder()\n\thttptransport.SetResponseHeader(key, val)(context.Background(), r)\n\tif want, have := val, r.Header().Get(key); want != have {\n\t\tt.Errorf(\"want %q, have %q\", want, have)\n\t}\n}\n\nfunc TestSetContentType(t *testing.T) {\n\tconst contentType = \"application/json\"\n\tr := httptest.NewRecorder()\n\thttptransport.SetContentType(contentType)(context.Background(), r)\n\tif want, have := contentType, r.Header().Get(\"Content-Type\"); want != have {\n\t\tt.Errorf(\"want %q, have %q\", want, have)\n\t}\n}\n"
  },
  {
    "path": "transport/http/server.go",
    "content": "package http\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/transport\"\n\t\"github.com/go-kit/log\"\n)\n\n// Server wraps an endpoint and implements http.Handler.\ntype Server struct {\n\te            endpoint.Endpoint\n\tdec          DecodeRequestFunc\n\tenc          EncodeResponseFunc\n\tbefore       []RequestFunc\n\tafter        []ServerResponseFunc\n\terrorEncoder ErrorEncoder\n\tfinalizer    []ServerFinalizerFunc\n\terrorHandler transport.ErrorHandler\n}\n\n// NewServer constructs a new server, which implements http.Handler and wraps\n// the provided endpoint.\nfunc NewServer(\n\te endpoint.Endpoint,\n\tdec DecodeRequestFunc,\n\tenc EncodeResponseFunc,\n\toptions ...ServerOption,\n) *Server {\n\ts := &Server{\n\t\te:            e,\n\t\tdec:          dec,\n\t\tenc:          enc,\n\t\terrorEncoder: DefaultErrorEncoder,\n\t\terrorHandler: transport.NewLogErrorHandler(log.NewNopLogger()),\n\t}\n\tfor _, option := range options {\n\t\toption(s)\n\t}\n\treturn s\n}\n\n// ServerOption sets an optional parameter for servers.\ntype ServerOption func(*Server)\n\n// ServerBefore functions are executed on the HTTP request object before the\n// request is decoded.\nfunc ServerBefore(before ...RequestFunc) ServerOption {\n\treturn func(s *Server) { s.before = append(s.before, before...) }\n}\n\n// ServerAfter functions are executed on the HTTP response writer after the\n// endpoint is invoked, but before anything is written to the client.\nfunc ServerAfter(after ...ServerResponseFunc) ServerOption {\n\treturn func(s *Server) { s.after = append(s.after, after...) }\n}\n\n// ServerErrorEncoder is used to encode errors to the http.ResponseWriter\n// whenever they're encountered in the processing of a request. Clients can\n// use this to provide custom error formatting and response codes. By default,\n// errors will be written with the DefaultErrorEncoder.\nfunc ServerErrorEncoder(ee ErrorEncoder) ServerOption {\n\treturn func(s *Server) { s.errorEncoder = ee }\n}\n\n// ServerErrorLogger is used to log non-terminal errors. By default, no errors\n// are logged. This is intended as a diagnostic measure. Finer-grained control\n// of error handling, including logging in more detail, should be performed in a\n// custom ServerErrorEncoder or ServerFinalizer, both of which have access to\n// the context.\n// Deprecated: Use ServerErrorHandler instead.\nfunc ServerErrorLogger(logger log.Logger) ServerOption {\n\treturn func(s *Server) { s.errorHandler = transport.NewLogErrorHandler(logger) }\n}\n\n// ServerErrorHandler is used to handle non-terminal errors. By default, non-terminal errors\n// are ignored. This is intended as a diagnostic measure. Finer-grained control\n// of error handling, including logging in more detail, should be performed in a\n// custom ServerErrorEncoder or ServerFinalizer, both of which have access to\n// the context.\nfunc ServerErrorHandler(errorHandler transport.ErrorHandler) ServerOption {\n\treturn func(s *Server) { s.errorHandler = errorHandler }\n}\n\n// ServerFinalizer is executed at the end of every HTTP request.\n// By default, no finalizer is registered.\nfunc ServerFinalizer(f ...ServerFinalizerFunc) ServerOption {\n\treturn func(s *Server) { s.finalizer = append(s.finalizer, f...) }\n}\n\n// ServeHTTP implements http.Handler.\nfunc (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tctx := r.Context()\n\n\tif len(s.finalizer) > 0 {\n\t\tiw := &interceptingWriter{w, http.StatusOK, 0}\n\t\tdefer func() {\n\t\t\tctx = context.WithValue(ctx, ContextKeyResponseHeaders, iw.Header())\n\t\t\tctx = context.WithValue(ctx, ContextKeyResponseSize, iw.written)\n\t\t\tfor _, f := range s.finalizer {\n\t\t\t\tf(ctx, iw.code, r)\n\t\t\t}\n\t\t}()\n\t\tw = iw.reimplementInterfaces()\n\t}\n\n\tfor _, f := range s.before {\n\t\tctx = f(ctx, r)\n\t}\n\n\trequest, err := s.dec(ctx, r)\n\tif err != nil {\n\t\ts.errorHandler.Handle(ctx, err)\n\t\ts.errorEncoder(ctx, err, w)\n\t\treturn\n\t}\n\n\tresponse, err := s.e(ctx, request)\n\tif err != nil {\n\t\ts.errorHandler.Handle(ctx, err)\n\t\ts.errorEncoder(ctx, err, w)\n\t\treturn\n\t}\n\n\tfor _, f := range s.after {\n\t\tctx = f(ctx, w)\n\t}\n\n\tif err := s.enc(ctx, w, response); err != nil {\n\t\ts.errorHandler.Handle(ctx, err)\n\t\ts.errorEncoder(ctx, err, w)\n\t\treturn\n\t}\n}\n\n// ErrorEncoder is responsible for encoding an error to the ResponseWriter.\n// Users are encouraged to use custom ErrorEncoders to encode HTTP errors to\n// their clients, and will likely want to pass and check for their own error\n// types. See the example shipping/handling service.\ntype ErrorEncoder func(ctx context.Context, err error, w http.ResponseWriter)\n\n// ServerFinalizerFunc can be used to perform work at the end of an HTTP\n// request, after the response has been written to the client. The principal\n// intended use is for request logging. In addition to the response code\n// provided in the function signature, additional response parameters are\n// provided in the context under keys with the ContextKeyResponse prefix.\ntype ServerFinalizerFunc func(ctx context.Context, code int, r *http.Request)\n\n// NopRequestDecoder is a DecodeRequestFunc that can be used for requests that do not\n// need to be decoded, and simply returns nil, nil.\nfunc NopRequestDecoder(ctx context.Context, r *http.Request) (interface{}, error) {\n\treturn nil, nil\n}\n\n// EncodeJSONResponse is a EncodeResponseFunc that serializes the response as a\n// JSON object to the ResponseWriter. Many JSON-over-HTTP services can use it as\n// a sensible default. If the response implements Headerer, the provided headers\n// will be applied to the response. If the response implements StatusCoder, the\n// provided StatusCode will be used instead of 200.\nfunc EncodeJSONResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {\n\tw.Header().Set(\"Content-Type\", \"application/json; charset=utf-8\")\n\tif headerer, ok := response.(Headerer); ok {\n\t\tfor k, values := range headerer.Headers() {\n\t\t\tfor _, v := range values {\n\t\t\t\tw.Header().Add(k, v)\n\t\t\t}\n\t\t}\n\t}\n\tcode := http.StatusOK\n\tif sc, ok := response.(StatusCoder); ok {\n\t\tcode = sc.StatusCode()\n\t}\n\tw.WriteHeader(code)\n\tif code == http.StatusNoContent {\n\t\treturn nil\n\t}\n\treturn json.NewEncoder(w).Encode(response)\n}\n\n// DefaultErrorEncoder writes the error to the ResponseWriter, by default a\n// content type of text/plain, a body of the plain text of the error, and a\n// status code of 500. If the error implements Headerer, the provided headers\n// will be applied to the response. If the error implements json.Marshaler, and\n// the marshaling succeeds, a content type of application/json and the JSON\n// encoded form of the error will be used. If the error implements StatusCoder,\n// the provided StatusCode will be used instead of 500.\nfunc DefaultErrorEncoder(_ context.Context, err error, w http.ResponseWriter) {\n\tcontentType, body := \"text/plain; charset=utf-8\", []byte(err.Error())\n\tif marshaler, ok := err.(json.Marshaler); ok {\n\t\tif jsonBody, marshalErr := marshaler.MarshalJSON(); marshalErr == nil {\n\t\t\tcontentType, body = \"application/json; charset=utf-8\", jsonBody\n\t\t}\n\t}\n\tw.Header().Set(\"Content-Type\", contentType)\n\tif headerer, ok := err.(Headerer); ok {\n\t\tfor k, values := range headerer.Headers() {\n\t\t\tfor _, v := range values {\n\t\t\t\tw.Header().Add(k, v)\n\t\t\t}\n\t\t}\n\t}\n\tcode := http.StatusInternalServerError\n\tif sc, ok := err.(StatusCoder); ok {\n\t\tcode = sc.StatusCode()\n\t}\n\tw.WriteHeader(code)\n\tw.Write(body)\n}\n\n// StatusCoder is checked by DefaultErrorEncoder. If an error value implements\n// StatusCoder, the StatusCode will be used when encoding the error. By default,\n// StatusInternalServerError (500) is used.\ntype StatusCoder interface {\n\tStatusCode() int\n}\n\n// Headerer is checked by DefaultErrorEncoder. If an error value implements\n// Headerer, the provided headers will be applied to the response writer, after\n// the Content-Type is set.\ntype Headerer interface {\n\tHeaders() http.Header\n}\n"
  },
  {
    "path": "transport/http/server_test.go",
    "content": "package http_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\thttptransport \"github.com/go-kit/kit/transport/http\"\n)\n\nfunc TestServerBadDecode(t *testing.T) {\n\thandler := httptransport.NewServer(\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil },\n\t\tfunc(context.Context, *http.Request) (interface{}, error) { return struct{}{}, errors.New(\"dang\") },\n\t\tfunc(context.Context, http.ResponseWriter, interface{}) error { return nil },\n\t)\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\tresp, _ := http.Get(server.URL)\n\tif want, have := http.StatusInternalServerError, resp.StatusCode; want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\nfunc TestServerBadEndpoint(t *testing.T) {\n\thandler := httptransport.NewServer(\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return struct{}{}, errors.New(\"dang\") },\n\t\tfunc(context.Context, *http.Request) (interface{}, error) { return struct{}{}, nil },\n\t\tfunc(context.Context, http.ResponseWriter, interface{}) error { return nil },\n\t)\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\tresp, _ := http.Get(server.URL)\n\tif want, have := http.StatusInternalServerError, resp.StatusCode; want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\nfunc TestServerBadEncode(t *testing.T) {\n\thandler := httptransport.NewServer(\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil },\n\t\tfunc(context.Context, *http.Request) (interface{}, error) { return struct{}{}, nil },\n\t\tfunc(context.Context, http.ResponseWriter, interface{}) error { return errors.New(\"dang\") },\n\t)\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\tresp, _ := http.Get(server.URL)\n\tif want, have := http.StatusInternalServerError, resp.StatusCode; want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\nfunc TestServerErrorEncoder(t *testing.T) {\n\terrTeapot := errors.New(\"teapot\")\n\tcode := func(err error) int {\n\t\tif errors.Is(err, errTeapot) {\n\t\t\treturn http.StatusTeapot\n\t\t}\n\t\treturn http.StatusInternalServerError\n\t}\n\thandler := httptransport.NewServer(\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return struct{}{}, errTeapot },\n\t\tfunc(context.Context, *http.Request) (interface{}, error) { return struct{}{}, nil },\n\t\tfunc(context.Context, http.ResponseWriter, interface{}) error { return nil },\n\t\thttptransport.ServerErrorEncoder(func(_ context.Context, err error, w http.ResponseWriter) { w.WriteHeader(code(err)) }),\n\t)\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\tresp, _ := http.Get(server.URL)\n\tif want, have := http.StatusTeapot, resp.StatusCode; want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\nfunc TestServerHappyPath(t *testing.T) {\n\tstep, response := testServer(t)\n\tstep()\n\tresp := <-response\n\tdefer resp.Body.Close()\n\tbuf, _ := ioutil.ReadAll(resp.Body)\n\tif want, have := http.StatusOK, resp.StatusCode; want != have {\n\t\tt.Errorf(\"want %d, have %d (%s)\", want, have, buf)\n\t}\n}\n\nfunc TestMultipleServerBefore(t *testing.T) {\n\tvar (\n\t\theaderKey    = \"X-Henlo-Lizer\"\n\t\theaderVal    = \"Helllo you stinky lizard\"\n\t\tstatusCode   = http.StatusTeapot\n\t\tresponseBody = \"go eat a fly ugly\\n\"\n\t\tdone         = make(chan struct{})\n\t)\n\thandler := httptransport.NewServer(\n\t\tendpoint.Nop,\n\t\tfunc(context.Context, *http.Request) (interface{}, error) {\n\t\t\treturn struct{}{}, nil\n\t\t},\n\t\tfunc(_ context.Context, w http.ResponseWriter, _ interface{}) error {\n\t\t\tw.Header().Set(headerKey, headerVal)\n\t\t\tw.WriteHeader(statusCode)\n\t\t\tw.Write([]byte(responseBody))\n\t\t\treturn nil\n\t\t},\n\t\thttptransport.ServerBefore(func(ctx context.Context, r *http.Request) context.Context {\n\t\t\tctx = context.WithValue(ctx, \"one\", 1)\n\n\t\t\treturn ctx\n\t\t}),\n\t\thttptransport.ServerBefore(func(ctx context.Context, r *http.Request) context.Context {\n\t\t\tif _, ok := ctx.Value(\"one\").(int); !ok {\n\t\t\t\tt.Error(\"Value was not set properly when multiple ServerBefores are used\")\n\t\t\t}\n\n\t\t\tclose(done)\n\t\t\treturn ctx\n\t\t}),\n\t)\n\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\tgo http.Get(server.URL)\n\n\tselect {\n\tcase <-done:\n\tcase <-time.After(time.Second):\n\t\tt.Fatal(\"timeout waiting for finalizer\")\n\t}\n}\n\nfunc TestMultipleServerAfter(t *testing.T) {\n\tvar (\n\t\theaderKey    = \"X-Henlo-Lizer\"\n\t\theaderVal    = \"Helllo you stinky lizard\"\n\t\tstatusCode   = http.StatusTeapot\n\t\tresponseBody = \"go eat a fly ugly\\n\"\n\t\tdone         = make(chan struct{})\n\t)\n\thandler := httptransport.NewServer(\n\t\tendpoint.Nop,\n\t\tfunc(context.Context, *http.Request) (interface{}, error) {\n\t\t\treturn struct{}{}, nil\n\t\t},\n\t\tfunc(_ context.Context, w http.ResponseWriter, _ interface{}) error {\n\t\t\tw.Header().Set(headerKey, headerVal)\n\t\t\tw.WriteHeader(statusCode)\n\t\t\tw.Write([]byte(responseBody))\n\t\t\treturn nil\n\t\t},\n\t\thttptransport.ServerAfter(func(ctx context.Context, w http.ResponseWriter) context.Context {\n\t\t\tctx = context.WithValue(ctx, \"one\", 1)\n\n\t\t\treturn ctx\n\t\t}),\n\t\thttptransport.ServerAfter(func(ctx context.Context, w http.ResponseWriter) context.Context {\n\t\t\tif _, ok := ctx.Value(\"one\").(int); !ok {\n\t\t\t\tt.Error(\"Value was not set properly when multiple ServerAfters are used\")\n\t\t\t}\n\n\t\t\tclose(done)\n\t\t\treturn ctx\n\t\t}),\n\t)\n\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\tgo http.Get(server.URL)\n\n\tselect {\n\tcase <-done:\n\tcase <-time.After(time.Second):\n\t\tt.Fatal(\"timeout waiting for finalizer\")\n\t}\n}\n\nfunc TestServerFinalizer(t *testing.T) {\n\tvar (\n\t\theaderKey    = \"X-Henlo-Lizer\"\n\t\theaderVal    = \"Helllo you stinky lizard\"\n\t\tstatusCode   = http.StatusTeapot\n\t\tresponseBody = \"go eat a fly ugly\\n\"\n\t\tdone         = make(chan struct{})\n\t)\n\thandler := httptransport.NewServer(\n\t\tendpoint.Nop,\n\t\tfunc(context.Context, *http.Request) (interface{}, error) {\n\t\t\treturn struct{}{}, nil\n\t\t},\n\t\tfunc(_ context.Context, w http.ResponseWriter, _ interface{}) error {\n\t\t\tw.Header().Set(headerKey, headerVal)\n\t\t\tw.WriteHeader(statusCode)\n\t\t\tw.Write([]byte(responseBody))\n\t\t\treturn nil\n\t\t},\n\t\thttptransport.ServerFinalizer(func(ctx context.Context, code int, _ *http.Request) {\n\t\t\tif want, have := statusCode, code; want != have {\n\t\t\t\tt.Errorf(\"StatusCode: want %d, have %d\", want, have)\n\t\t\t}\n\n\t\t\tresponseHeader := ctx.Value(httptransport.ContextKeyResponseHeaders).(http.Header)\n\t\t\tif want, have := headerVal, responseHeader.Get(headerKey); want != have {\n\t\t\t\tt.Errorf(\"%s: want %q, have %q\", headerKey, want, have)\n\t\t\t}\n\n\t\t\tresponseSize := ctx.Value(httptransport.ContextKeyResponseSize).(int64)\n\t\t\tif want, have := int64(len(responseBody)), responseSize; want != have {\n\t\t\t\tt.Errorf(\"response size: want %d, have %d\", want, have)\n\t\t\t}\n\n\t\t\tclose(done)\n\t\t}),\n\t)\n\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\tgo http.Get(server.URL)\n\n\tselect {\n\tcase <-done:\n\tcase <-time.After(time.Second):\n\t\tt.Fatal(\"timeout waiting for finalizer\")\n\t}\n}\n\ntype enhancedResponse struct {\n\tFoo string `json:\"foo\"`\n}\n\nfunc (e enhancedResponse) StatusCode() int      { return http.StatusPaymentRequired }\nfunc (e enhancedResponse) Headers() http.Header { return http.Header{\"X-Edward\": []string{\"Snowden\"}} }\n\nfunc TestEncodeJSONResponse(t *testing.T) {\n\thandler := httptransport.NewServer(\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return enhancedResponse{Foo: \"bar\"}, nil },\n\t\tfunc(context.Context, *http.Request) (interface{}, error) { return struct{}{}, nil },\n\t\thttptransport.EncodeJSONResponse,\n\t)\n\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\n\tresp, err := http.Get(server.URL)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif want, have := http.StatusPaymentRequired, resp.StatusCode; want != have {\n\t\tt.Errorf(\"StatusCode: want %d, have %d\", want, have)\n\t}\n\tif want, have := \"Snowden\", resp.Header.Get(\"X-Edward\"); want != have {\n\t\tt.Errorf(\"X-Edward: want %q, have %q\", want, have)\n\t}\n\tbuf, _ := ioutil.ReadAll(resp.Body)\n\tif want, have := `{\"foo\":\"bar\"}`, strings.TrimSpace(string(buf)); want != have {\n\t\tt.Errorf(\"Body: want %s, have %s\", want, have)\n\t}\n}\n\ntype multiHeaderResponse struct{}\n\nfunc (_ multiHeaderResponse) Headers() http.Header {\n\treturn http.Header{\"Vary\": []string{\"Origin\", \"User-Agent\"}}\n}\n\nfunc TestAddMultipleHeaders(t *testing.T) {\n\thandler := httptransport.NewServer(\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return multiHeaderResponse{}, nil },\n\t\tfunc(context.Context, *http.Request) (interface{}, error) { return struct{}{}, nil },\n\t\thttptransport.EncodeJSONResponse,\n\t)\n\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\n\tresp, err := http.Get(server.URL)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\texpect := map[string]map[string]struct{}{\"Vary\": map[string]struct{}{\"Origin\": struct{}{}, \"User-Agent\": struct{}{}}}\n\tfor k, vls := range resp.Header {\n\t\tfor _, v := range vls {\n\t\t\tdelete((expect[k]), v)\n\t\t}\n\t\tif len(expect[k]) != 0 {\n\t\t\tt.Errorf(\"Header: unexpected header %s: %v\", k, expect[k])\n\t\t}\n\t}\n}\n\ntype multiHeaderResponseError struct {\n\tmultiHeaderResponse\n\tmsg string\n}\n\nfunc (m multiHeaderResponseError) Error() string {\n\treturn m.msg\n}\n\nfunc TestAddMultipleHeadersErrorEncoder(t *testing.T) {\n\terrStr := \"oh no\"\n\thandler := httptransport.NewServer(\n\t\tfunc(context.Context, interface{}) (interface{}, error) {\n\t\t\treturn nil, multiHeaderResponseError{msg: errStr}\n\t\t},\n\t\tfunc(context.Context, *http.Request) (interface{}, error) { return struct{}{}, nil },\n\t\thttptransport.EncodeJSONResponse,\n\t)\n\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\n\tresp, err := http.Get(server.URL)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\texpect := map[string]map[string]struct{}{\"Vary\": map[string]struct{}{\"Origin\": struct{}{}, \"User-Agent\": struct{}{}}}\n\tfor k, vls := range resp.Header {\n\t\tfor _, v := range vls {\n\t\t\tdelete((expect[k]), v)\n\t\t}\n\t\tif len(expect[k]) != 0 {\n\t\t\tt.Errorf(\"Header: unexpected header %s: %v\", k, expect[k])\n\t\t}\n\t}\n\tif b, _ := ioutil.ReadAll(resp.Body); errStr != string(b) {\n\t\tt.Errorf(\"ErrorEncoder: got: %q, expected: %q\", b, errStr)\n\t}\n}\n\ntype noContentResponse struct{}\n\nfunc (e noContentResponse) StatusCode() int { return http.StatusNoContent }\n\nfunc TestEncodeNoContent(t *testing.T) {\n\thandler := httptransport.NewServer(\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return noContentResponse{}, nil },\n\t\tfunc(context.Context, *http.Request) (interface{}, error) { return struct{}{}, nil },\n\t\thttptransport.EncodeJSONResponse,\n\t)\n\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\n\tresp, err := http.Get(server.URL)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif want, have := http.StatusNoContent, resp.StatusCode; want != have {\n\t\tt.Errorf(\"StatusCode: want %d, have %d\", want, have)\n\t}\n\tbuf, _ := ioutil.ReadAll(resp.Body)\n\tif want, have := 0, len(buf); want != have {\n\t\tt.Errorf(\"Body: want no content, have %d bytes\", have)\n\t}\n}\n\ntype enhancedError struct{}\n\nfunc (e enhancedError) Error() string                { return \"enhanced error\" }\nfunc (e enhancedError) StatusCode() int              { return http.StatusTeapot }\nfunc (e enhancedError) MarshalJSON() ([]byte, error) { return []byte(`{\"err\":\"enhanced\"}`), nil }\nfunc (e enhancedError) Headers() http.Header         { return http.Header{\"X-Enhanced\": []string{\"1\"}} }\n\nfunc TestEnhancedError(t *testing.T) {\n\thandler := httptransport.NewServer(\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return nil, enhancedError{} },\n\t\tfunc(context.Context, *http.Request) (interface{}, error) { return struct{}{}, nil },\n\t\tfunc(_ context.Context, w http.ResponseWriter, _ interface{}) error { return nil },\n\t)\n\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\n\tresp, err := http.Get(server.URL)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer resp.Body.Close()\n\tif want, have := http.StatusTeapot, resp.StatusCode; want != have {\n\t\tt.Errorf(\"StatusCode: want %d, have %d\", want, have)\n\t}\n\tif want, have := \"1\", resp.Header.Get(\"X-Enhanced\"); want != have {\n\t\tt.Errorf(\"X-Enhanced: want %q, have %q\", want, have)\n\t}\n\tbuf, _ := ioutil.ReadAll(resp.Body)\n\tif want, have := `{\"err\":\"enhanced\"}`, strings.TrimSpace(string(buf)); want != have {\n\t\tt.Errorf(\"Body: want %s, have %s\", want, have)\n\t}\n}\n\nfunc TestNoOpRequestDecoder(t *testing.T) {\n\tresw := httptest.NewRecorder()\n\treq, err := http.NewRequest(http.MethodGet, \"/\", nil)\n\tif err != nil {\n\t\tt.Error(\"Failed to create request\")\n\t}\n\thandler := httptransport.NewServer(\n\t\tfunc(ctx context.Context, request interface{}) (interface{}, error) {\n\t\t\tif request != nil {\n\t\t\t\tt.Error(\"Expected nil request in endpoint when using NopRequestDecoder\")\n\t\t\t}\n\t\t\treturn nil, nil\n\t\t},\n\t\thttptransport.NopRequestDecoder,\n\t\thttptransport.EncodeJSONResponse,\n\t)\n\thandler.ServeHTTP(resw, req)\n\tif resw.Code != http.StatusOK {\n\t\tt.Errorf(\"Expected status code %d but got %d\", http.StatusOK, resw.Code)\n\t}\n}\n\nfunc testServer(t *testing.T) (step func(), resp <-chan *http.Response) {\n\tvar (\n\t\tstepch   = make(chan bool)\n\t\tendpoint = func(context.Context, interface{}) (interface{}, error) { <-stepch; return struct{}{}, nil }\n\t\tresponse = make(chan *http.Response)\n\t\thandler  = httptransport.NewServer(\n\t\t\tendpoint,\n\t\t\tfunc(context.Context, *http.Request) (interface{}, error) { return struct{}{}, nil },\n\t\t\tfunc(context.Context, http.ResponseWriter, interface{}) error { return nil },\n\t\t\thttptransport.ServerBefore(func(ctx context.Context, r *http.Request) context.Context { return ctx }),\n\t\t\thttptransport.ServerAfter(func(ctx context.Context, w http.ResponseWriter) context.Context { return ctx }),\n\t\t)\n\t)\n\tgo func() {\n\t\tserver := httptest.NewServer(handler)\n\t\tdefer server.Close()\n\t\tresp, err := http.Get(server.URL)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\treturn\n\t\t}\n\t\tresponse <- resp\n\t}()\n\treturn func() { stepch <- true }, response\n}\n"
  },
  {
    "path": "transport/httprp/doc.go",
    "content": "// Package httprp provides an HTTP reverse-proxy transport. HTTP handlers that\n// need to proxy requests to another HTTP service can do so with this package by\n// specifying the URL to forward the request to.\npackage httprp\n"
  },
  {
    "path": "transport/httprp/server.go",
    "content": "package httprp\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"net/url\"\n)\n\n// RequestFunc may take information from an HTTP request and put it into a\n// request context. BeforeFuncs are executed prior to invoking the\n// endpoint.\ntype RequestFunc func(context.Context, *http.Request) context.Context\n\n// Server is a proxying request handler.\ntype Server struct {\n\tproxy        http.Handler\n\tbefore       []RequestFunc\n\terrorEncoder func(w http.ResponseWriter, err error)\n}\n\n// NewServer constructs a new server that implements http.Server and will proxy\n// requests to the given base URL using its scheme, host, and base path.\n// If the target's path is \"/base\" and the incoming request was for \"/dir\",\n// the target request will be for /base/dir.\nfunc NewServer(\n\tbaseURL *url.URL,\n\toptions ...ServerOption,\n) *Server {\n\ts := &Server{\n\t\tproxy: httputil.NewSingleHostReverseProxy(baseURL),\n\t}\n\tfor _, option := range options {\n\t\toption(s)\n\t}\n\treturn s\n}\n\n// ServerOption sets an optional parameter for servers.\ntype ServerOption func(*Server)\n\n// ServerBefore functions are executed on the HTTP request object before the\n// request is decoded.\nfunc ServerBefore(before ...RequestFunc) ServerOption {\n\treturn func(s *Server) { s.before = append(s.before, before...) }\n}\n\n// ServeHTTP implements http.Handler.\nfunc (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tctx := r.Context()\n\n\tfor _, f := range s.before {\n\t\tctx = f(ctx, r)\n\t}\n\n\ts.proxy.ServeHTTP(w, r)\n}\n"
  },
  {
    "path": "transport/httprp/server_test.go",
    "content": "package httprp_test\n\nimport (\n\t\"context\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\n\thttptransport \"github.com/go-kit/kit/transport/httprp\"\n)\n\nfunc TestServerHappyPathSingleServer(t *testing.T) {\n\toriginServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(http.StatusOK)\n\t\tw.Write([]byte(\"hey\"))\n\t}))\n\tdefer originServer.Close()\n\toriginURL, _ := url.Parse(originServer.URL)\n\n\thandler := httptransport.NewServer(\n\t\toriginURL,\n\t)\n\tproxyServer := httptest.NewServer(handler)\n\tdefer proxyServer.Close()\n\n\tresp, _ := http.Get(proxyServer.URL)\n\tif want, have := http.StatusOK, resp.StatusCode; want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\tresponseBody, _ := ioutil.ReadAll(resp.Body)\n\tif want, have := \"hey\", string(responseBody); want != have {\n\t\tt.Errorf(\"want %q, have %q\", want, have)\n\t}\n}\n\nfunc TestServerHappyPathSingleServerWithServerOptions(t *testing.T) {\n\tconst (\n\t\theaderKey = \"X-TEST-HEADER\"\n\t\theaderVal = \"go-kit-proxy\"\n\t)\n\n\toriginServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tif want, have := headerVal, r.Header.Get(headerKey); want != have {\n\t\t\tt.Errorf(\"want %q, have %q\", want, have)\n\t\t}\n\n\t\tw.WriteHeader(http.StatusOK)\n\t\tw.Write([]byte(\"hey\"))\n\t}))\n\tdefer originServer.Close()\n\toriginURL, _ := url.Parse(originServer.URL)\n\n\thandler := httptransport.NewServer(\n\t\toriginURL,\n\t\thttptransport.ServerBefore(func(ctx context.Context, r *http.Request) context.Context {\n\t\t\tr.Header.Add(headerKey, headerVal)\n\t\t\treturn ctx\n\t\t}),\n\t)\n\tproxyServer := httptest.NewServer(handler)\n\tdefer proxyServer.Close()\n\n\tresp, _ := http.Get(proxyServer.URL)\n\tif want, have := http.StatusOK, resp.StatusCode; want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\tresponseBody, _ := ioutil.ReadAll(resp.Body)\n\tif want, have := \"hey\", string(responseBody); want != have {\n\t\tt.Errorf(\"want %q, have %q\", want, have)\n\t}\n}\n\nfunc TestServerOriginServerNotFoundResponse(t *testing.T) {\n\toriginServer := httptest.NewServer(http.NotFoundHandler())\n\tdefer originServer.Close()\n\toriginURL, _ := url.Parse(originServer.URL)\n\n\thandler := httptransport.NewServer(\n\t\toriginURL,\n\t)\n\tproxyServer := httptest.NewServer(handler)\n\tdefer proxyServer.Close()\n\n\tresp, _ := http.Get(proxyServer.URL)\n\tif want, have := http.StatusNotFound, resp.StatusCode; want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n}\n\nfunc TestServerOriginServerUnreachable(t *testing.T) {\n\t// create a server, then promptly shut it down\n\toriginServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(http.StatusOK)\n\t}))\n\toriginURL, _ := url.Parse(originServer.URL)\n\toriginServer.Close()\n\n\thandler := httptransport.NewServer(\n\t\toriginURL,\n\t)\n\tproxyServer := httptest.NewServer(handler)\n\tdefer proxyServer.Close()\n\n\tresp, _ := http.Get(proxyServer.URL)\n\tswitch resp.StatusCode {\n\tcase http.StatusBadGateway: // go1.7 and beyond\n\t\tbreak\n\tcase http.StatusInternalServerError: // to go1.7\n\t\tbreak\n\tdefault:\n\t\tt.Errorf(\"want %d or %d, have %d\", http.StatusBadGateway, http.StatusInternalServerError, resp.StatusCode)\n\t}\n}\n\nfunc TestMultipleServerBefore(t *testing.T) {\n\tconst (\n\t\theaderKey = \"X-TEST-HEADER\"\n\t\theaderVal = \"go-kit-proxy\"\n\t)\n\n\toriginServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tif want, have := headerVal, r.Header.Get(headerKey); want != have {\n\t\t\tt.Errorf(\"want %q, have %q\", want, have)\n\t\t}\n\n\t\tw.WriteHeader(http.StatusOK)\n\t\tw.Write([]byte(\"hey\"))\n\t}))\n\tdefer originServer.Close()\n\toriginURL, _ := url.Parse(originServer.URL)\n\n\thandler := httptransport.NewServer(\n\t\toriginURL,\n\t\thttptransport.ServerBefore(func(ctx context.Context, r *http.Request) context.Context {\n\t\t\tr.Header.Add(headerKey, headerVal)\n\t\t\treturn ctx\n\t\t}),\n\t\thttptransport.ServerBefore(func(ctx context.Context, r *http.Request) context.Context {\n\t\t\treturn ctx\n\t\t}),\n\t)\n\tproxyServer := httptest.NewServer(handler)\n\tdefer proxyServer.Close()\n\n\tresp, _ := http.Get(proxyServer.URL)\n\tif want, have := http.StatusOK, resp.StatusCode; want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\tresponseBody, _ := ioutil.ReadAll(resp.Body)\n\tif want, have := \"hey\", string(responseBody); want != have {\n\t\tt.Errorf(\"want %q, have %q\", want, have)\n\t}\n}\n"
  },
  {
    "path": "transport/nats/doc.go",
    "content": "// Package nats provides a NATS transport.\npackage nats\n"
  },
  {
    "path": "transport/nats/encode_decode.go",
    "content": "package nats\n\nimport (\n\t\"context\"\n\n\t\"github.com/nats-io/nats.go\"\n)\n\n// DecodeRequestFunc extracts a user-domain request object from a publisher\n// request object. It's designed to be used in NATS subscribers, for subscriber-side\n// endpoints. One straightforward DecodeRequestFunc could be something that\n// JSON decodes from the request body to the concrete response type.\ntype DecodeRequestFunc func(context.Context, *nats.Msg) (request interface{}, err error)\n\n// EncodeRequestFunc encodes the passed request object into the NATS request\n// object. It's designed to be used in NATS publishers, for publisher-side\n// endpoints. One straightforward EncodeRequestFunc could something that JSON\n// encodes the object directly to the request payload.\ntype EncodeRequestFunc func(context.Context, *nats.Msg, interface{}) error\n\n// EncodeResponseFunc encodes the passed response object to the subscriber reply.\n// It's designed to be used in NATS subscribers, for subscriber-side\n// endpoints. One straightforward EncodeResponseFunc could be something that\n// JSON encodes the object directly to the response body.\ntype EncodeResponseFunc func(context.Context, string, *nats.Conn, interface{}) error\n\n// DecodeResponseFunc extracts a user-domain response object from an NATS\n// response object. It's designed to be used in NATS publisher, for publisher-side\n// endpoints. One straightforward DecodeResponseFunc could be something that\n// JSON decodes from the response payload to the concrete response type.\ntype DecodeResponseFunc func(context.Context, *nats.Msg) (response interface{}, err error)\n\n"
  },
  {
    "path": "transport/nats/publisher.go",
    "content": "package nats\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/nats-io/nats.go\"\n\t\"time\"\n)\n\n// Publisher wraps a URL and provides a method that implements endpoint.Endpoint.\ntype Publisher struct {\n\tpublisher *nats.Conn\n\tsubject   string\n\tenc       EncodeRequestFunc\n\tdec       DecodeResponseFunc\n\tbefore    []RequestFunc\n\tafter     []PublisherResponseFunc\n\ttimeout   time.Duration\n}\n\n// NewPublisher constructs a usable Publisher for a single remote method.\nfunc NewPublisher(\n\tpublisher *nats.Conn,\n\tsubject string,\n\tenc EncodeRequestFunc,\n\tdec DecodeResponseFunc,\n\toptions ...PublisherOption,\n) *Publisher {\n\tp := &Publisher{\n\t\tpublisher: publisher,\n\t\tsubject:   subject,\n\t\tenc:       enc,\n\t\tdec:       dec,\n\t\ttimeout:   10 * time.Second,\n\t}\n\tfor _, option := range options {\n\t\toption(p)\n\t}\n\treturn p\n}\n\n// PublisherOption sets an optional parameter for clients.\ntype PublisherOption func(*Publisher)\n\n// PublisherBefore sets the RequestFuncs that are applied to the outgoing NATS\n// request before it's invoked.\nfunc PublisherBefore(before ...RequestFunc) PublisherOption {\n\treturn func(p *Publisher) { p.before = append(p.before, before...) }\n}\n\n// PublisherAfter sets the ClientResponseFuncs applied to the incoming NATS\n// request prior to it being decoded. This is useful for obtaining anything off\n// of the response and adding onto the context prior to decoding.\nfunc PublisherAfter(after ...PublisherResponseFunc) PublisherOption {\n\treturn func(p *Publisher) { p.after = append(p.after, after...) }\n}\n\n// PublisherTimeout sets the available timeout for NATS request.\nfunc PublisherTimeout(timeout time.Duration) PublisherOption {\n\treturn func(p *Publisher) { p.timeout = timeout }\n}\n\n// Endpoint returns a usable endpoint that invokes the remote endpoint.\nfunc (p Publisher) Endpoint() endpoint.Endpoint {\n\treturn func(ctx context.Context, request interface{}) (interface{}, error) {\n\t\tctx, cancel := context.WithTimeout(ctx, p.timeout)\n\t\tdefer cancel()\n\n\t\tmsg := nats.Msg{Subject: p.subject}\n\n\t\tif err := p.enc(ctx, &msg, request); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tfor _, f := range p.before {\n\t\t\tctx = f(ctx, &msg)\n\t\t}\n\n\t\tresp, err := p.publisher.RequestWithContext(ctx, msg.Subject, msg.Data)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tfor _, f := range p.after {\n\t\t\tctx = f(ctx, resp)\n\t\t}\n\n\t\tresponse, err := p.dec(ctx, resp)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn response, nil\n\t}\n}\n\n// EncodeJSONRequest is an EncodeRequestFunc that serializes the request as a\n// JSON object to the Data of the Msg. Many JSON-over-NATS services can use it as\n// a sensible default.\nfunc EncodeJSONRequest(_ context.Context, msg *nats.Msg, request interface{}) error {\n\tb, err := json.Marshal(request)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tmsg.Data = b\n\n\treturn nil\n}\n"
  },
  {
    "path": "transport/nats/publisher_test.go",
    "content": "package nats_test\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\tnatstransport \"github.com/go-kit/kit/transport/nats\"\n\t\"github.com/nats-io/nats.go\"\n)\n\nfunc TestPublisher(t *testing.T) {\n\tvar (\n\t\ttestdata = \"testdata\"\n\t\tencode   = func(context.Context, *nats.Msg, interface{}) error { return nil }\n\t\tdecode   = func(_ context.Context, msg *nats.Msg) (interface{}, error) {\n\t\t\treturn TestResponse{string(msg.Data), \"\"}, nil\n\t\t}\n\t)\n\n\ts, c := newNATSConn(t)\n\tdefer func() { s.Shutdown(); s.WaitForShutdown() }()\n\tdefer c.Close()\n\n\tsub, err := c.QueueSubscribe(\"natstransport.test\", \"natstransport\", func(msg *nats.Msg) {\n\t\tif err := c.Publish(msg.Reply, []byte(testdata)); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer sub.Unsubscribe()\n\n\tpublisher := natstransport.NewPublisher(\n\t\tc,\n\t\t\"natstransport.test\",\n\t\tencode,\n\t\tdecode,\n\t)\n\n\tres, err := publisher.Endpoint()(context.Background(), struct{}{})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tresponse, ok := res.(TestResponse)\n\tif !ok {\n\t\tt.Fatal(\"response should be TestResponse\")\n\t}\n\tif want, have := testdata, response.String; want != have {\n\t\tt.Errorf(\"want %q, have %q\", want, have)\n\t}\n\n}\n\nfunc TestPublisherBefore(t *testing.T) {\n\tvar (\n\t\ttestdata = \"testdata\"\n\t\tencode   = func(context.Context, *nats.Msg, interface{}) error { return nil }\n\t\tdecode   = func(_ context.Context, msg *nats.Msg) (interface{}, error) {\n\t\t\treturn TestResponse{string(msg.Data), \"\"}, nil\n\t\t}\n\t)\n\n\ts, c := newNATSConn(t)\n\tdefer func() { s.Shutdown(); s.WaitForShutdown() }()\n\tdefer c.Close()\n\n\tsub, err := c.QueueSubscribe(\"natstransport.test\", \"natstransport\", func(msg *nats.Msg) {\n\t\tif err := c.Publish(msg.Reply, msg.Data); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer sub.Unsubscribe()\n\n\tpublisher := natstransport.NewPublisher(\n\t\tc,\n\t\t\"natstransport.test\",\n\t\tencode,\n\t\tdecode,\n\t\tnatstransport.PublisherBefore(func(ctx context.Context, msg *nats.Msg) context.Context {\n\t\t\tmsg.Data = []byte(strings.ToUpper(string(testdata)))\n\t\t\treturn ctx\n\t\t}),\n\t)\n\n\tres, err := publisher.Endpoint()(context.Background(), struct{}{})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tresponse, ok := res.(TestResponse)\n\tif !ok {\n\t\tt.Fatal(\"response should be TestResponse\")\n\t}\n\tif want, have := strings.ToUpper(testdata), response.String; want != have {\n\t\tt.Errorf(\"want %q, have %q\", want, have)\n\t}\n\n}\n\nfunc TestPublisherAfter(t *testing.T) {\n\tvar (\n\t\ttestdata = \"testdata\"\n\t\tencode   = func(context.Context, *nats.Msg, interface{}) error { return nil }\n\t\tdecode   = func(_ context.Context, msg *nats.Msg) (interface{}, error) {\n\t\t\treturn TestResponse{string(msg.Data), \"\"}, nil\n\t\t}\n\t)\n\n\ts, c := newNATSConn(t)\n\tdefer func() { s.Shutdown(); s.WaitForShutdown() }()\n\tdefer c.Close()\n\n\tsub, err := c.QueueSubscribe(\"natstransport.test\", \"natstransport\", func(msg *nats.Msg) {\n\t\tif err := c.Publish(msg.Reply, []byte(testdata)); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer sub.Unsubscribe()\n\n\tpublisher := natstransport.NewPublisher(\n\t\tc,\n\t\t\"natstransport.test\",\n\t\tencode,\n\t\tdecode,\n\t\tnatstransport.PublisherAfter(func(ctx context.Context, msg *nats.Msg) context.Context {\n\t\t\tmsg.Data = []byte(strings.ToUpper(string(msg.Data)))\n\t\t\treturn ctx\n\t\t}),\n\t)\n\n\tres, err := publisher.Endpoint()(context.Background(), struct{}{})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tresponse, ok := res.(TestResponse)\n\tif !ok {\n\t\tt.Fatal(\"response should be TestResponse\")\n\t}\n\tif want, have := strings.ToUpper(testdata), response.String; want != have {\n\t\tt.Errorf(\"want %q, have %q\", want, have)\n\t}\n\n}\n\nfunc TestPublisherTimeout(t *testing.T) {\n\tvar (\n\t\tencode = func(context.Context, *nats.Msg, interface{}) error { return nil }\n\t\tdecode = func(_ context.Context, msg *nats.Msg) (interface{}, error) {\n\t\t\treturn TestResponse{string(msg.Data), \"\"}, nil\n\t\t}\n\t)\n\n\ts, c := newNATSConn(t)\n\tdefer func() { s.Shutdown(); s.WaitForShutdown() }()\n\tdefer c.Close()\n\n\tch := make(chan struct{})\n\tdefer close(ch)\n\n\tsub, err := c.QueueSubscribe(\"natstransport.test\", \"natstransport\", func(msg *nats.Msg) {\n\t\t<-ch\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer sub.Unsubscribe()\n\n\tpublisher := natstransport.NewPublisher(\n\t\tc,\n\t\t\"natstransport.test\",\n\t\tencode,\n\t\tdecode,\n\t\tnatstransport.PublisherTimeout(time.Second),\n\t)\n\n\t_, err = publisher.Endpoint()(context.Background(), struct{}{})\n\tif err != context.DeadlineExceeded {\n\t\tt.Errorf(\"want %s, have %s\", context.DeadlineExceeded, err)\n\t}\n}\n\nfunc TestPublisherCancellation(t *testing.T) {\n\tvar (\n\t\ttestdata = \"testdata\"\n\t\tencode   = func(context.Context, *nats.Msg, interface{}) error { return nil }\n\t\tdecode   = func(_ context.Context, msg *nats.Msg) (interface{}, error) {\n\t\t\treturn TestResponse{string(msg.Data), \"\"}, nil\n\t\t}\n\t)\n\n\ts, c := newNATSConn(t)\n\tdefer func() { s.Shutdown(); s.WaitForShutdown() }()\n\tdefer c.Close()\n\n\tsub, err := c.QueueSubscribe(\"natstransport.test\", \"natstransport\", func(msg *nats.Msg) {\n\t\tif err := c.Publish(msg.Reply, []byte(testdata)); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer sub.Unsubscribe()\n\n\tpublisher := natstransport.NewPublisher(\n\t\tc,\n\t\t\"natstransport.test\",\n\t\tencode,\n\t\tdecode,\n\t)\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tcancel()\n\n\t_, err = publisher.Endpoint()(ctx, struct{}{})\n\tif err != context.Canceled {\n\t\tt.Errorf(\"want %s, have %s\", context.Canceled, err)\n\t}\n}\n\nfunc TestEncodeJSONRequest(t *testing.T) {\n\tvar data string\n\n\ts, c := newNATSConn(t)\n\tdefer func() { s.Shutdown(); s.WaitForShutdown() }()\n\tdefer c.Close()\n\n\tsub, err := c.QueueSubscribe(\"natstransport.test\", \"natstransport\", func(msg *nats.Msg) {\n\t\tdata = string(msg.Data)\n\n\t\tif err := c.Publish(msg.Reply, []byte(\"\")); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer sub.Unsubscribe()\n\n\tpublisher := natstransport.NewPublisher(\n\t\tc,\n\t\t\"natstransport.test\",\n\t\tnatstransport.EncodeJSONRequest,\n\t\tfunc(context.Context, *nats.Msg) (interface{}, error) { return nil, nil },\n\t).Endpoint()\n\n\tfor _, test := range []struct {\n\t\tvalue interface{}\n\t\tbody  string\n\t}{\n\t\t{nil, \"null\"},\n\t\t{12, \"12\"},\n\t\t{1.2, \"1.2\"},\n\t\t{true, \"true\"},\n\t\t{\"test\", \"\\\"test\\\"\"},\n\t\t{struct {\n\t\t\tFoo string `json:\"foo\"`\n\t\t}{\"foo\"}, \"{\\\"foo\\\":\\\"foo\\\"}\"},\n\t} {\n\t\tif _, err := publisher(context.Background(), test.value); err != nil {\n\t\t\tt.Fatal(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tif data != test.body {\n\t\t\tt.Errorf(\"%v: actual %#v, expected %#v\", test.value, data, test.body)\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "transport/nats/request_response_funcs.go",
    "content": "package nats\n\nimport (\n\t\"context\"\n\n\t\"github.com/nats-io/nats.go\"\n)\n\n// RequestFunc may take information from a publisher request and put it into a\n// request context. In Subscribers, RequestFuncs are executed prior to invoking the\n// endpoint.\ntype RequestFunc func(context.Context, *nats.Msg) context.Context\n\n// SubscriberResponseFunc may take information from a request context and use it to\n// manipulate a Publisher. SubscriberResponseFuncs are only executed in\n// subscribers, after invoking the endpoint but prior to publishing a reply.\ntype SubscriberResponseFunc func(context.Context, *nats.Conn) context.Context\n\n// PublisherResponseFunc may take information from an NATS request and make the\n// response available for consumption. ClientResponseFuncs are only executed in\n// clients, after a request has been made, but prior to it being decoded.\ntype PublisherResponseFunc func(context.Context, *nats.Msg) context.Context\n"
  },
  {
    "path": "transport/nats/subscriber.go",
    "content": "package nats\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\t\"github.com/go-kit/kit/transport\"\n\t\"github.com/go-kit/log\"\n\n\t\"github.com/nats-io/nats.go\"\n)\n\n// Subscriber wraps an endpoint and provides nats.MsgHandler.\ntype Subscriber struct {\n\te            endpoint.Endpoint\n\tdec          DecodeRequestFunc\n\tenc          EncodeResponseFunc\n\tbefore       []RequestFunc\n\tafter        []SubscriberResponseFunc\n\terrorEncoder ErrorEncoder\n\tfinalizer    []SubscriberFinalizerFunc\n\terrorHandler transport.ErrorHandler\n}\n\n// NewSubscriber constructs a new subscriber, which provides nats.MsgHandler and wraps\n// the provided endpoint.\nfunc NewSubscriber(\n\te endpoint.Endpoint,\n\tdec DecodeRequestFunc,\n\tenc EncodeResponseFunc,\n\toptions ...SubscriberOption,\n) *Subscriber {\n\ts := &Subscriber{\n\t\te:            e,\n\t\tdec:          dec,\n\t\tenc:          enc,\n\t\terrorEncoder: DefaultErrorEncoder,\n\t\terrorHandler: transport.NewLogErrorHandler(log.NewNopLogger()),\n\t}\n\tfor _, option := range options {\n\t\toption(s)\n\t}\n\treturn s\n}\n\n// SubscriberOption sets an optional parameter for subscribers.\ntype SubscriberOption func(*Subscriber)\n\n// SubscriberBefore functions are executed on the publisher request object before the\n// request is decoded.\nfunc SubscriberBefore(before ...RequestFunc) SubscriberOption {\n\treturn func(s *Subscriber) { s.before = append(s.before, before...) }\n}\n\n// SubscriberAfter functions are executed on the subscriber reply after the\n// endpoint is invoked, but before anything is published to the reply.\nfunc SubscriberAfter(after ...SubscriberResponseFunc) SubscriberOption {\n\treturn func(s *Subscriber) { s.after = append(s.after, after...) }\n}\n\n// SubscriberErrorEncoder is used to encode errors to the subscriber reply\n// whenever they're encountered in the processing of a request. Clients can\n// use this to provide custom error formatting. By default,\n// errors will be published with the DefaultErrorEncoder.\nfunc SubscriberErrorEncoder(ee ErrorEncoder) SubscriberOption {\n\treturn func(s *Subscriber) { s.errorEncoder = ee }\n}\n\n// SubscriberErrorLogger is used to log non-terminal errors. By default, no errors\n// are logged. This is intended as a diagnostic measure. Finer-grained control\n// of error handling, including logging in more detail, should be performed in a\n// custom SubscriberErrorEncoder which has access to the context.\n// Deprecated: Use SubscriberErrorHandler instead.\nfunc SubscriberErrorLogger(logger log.Logger) SubscriberOption {\n\treturn func(s *Subscriber) { s.errorHandler = transport.NewLogErrorHandler(logger) }\n}\n\n// SubscriberErrorHandler is used to handle non-terminal errors. By default, non-terminal errors\n// are ignored. This is intended as a diagnostic measure. Finer-grained control\n// of error handling, including logging in more detail, should be performed in a\n// custom SubscriberErrorEncoder which has access to the context.\nfunc SubscriberErrorHandler(errorHandler transport.ErrorHandler) SubscriberOption {\n\treturn func(s *Subscriber) { s.errorHandler = errorHandler }\n}\n\n// SubscriberFinalizer is executed at the end of every request from a publisher through NATS.\n// By default, no finalizer is registered.\nfunc SubscriberFinalizer(f ...SubscriberFinalizerFunc) SubscriberOption {\n\treturn func(s *Subscriber) { s.finalizer = f }\n}\n\n// ServeMsg provides nats.MsgHandler.\nfunc (s Subscriber) ServeMsg(nc *nats.Conn) func(msg *nats.Msg) {\n\treturn func(msg *nats.Msg) {\n\t\tctx, cancel := context.WithCancel(context.Background())\n\t\tdefer cancel()\n\n\t\tif len(s.finalizer) > 0 {\n\t\t\tdefer func() {\n\t\t\t\tfor _, f := range s.finalizer {\n\t\t\t\t\tf(ctx, msg)\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\n\t\tfor _, f := range s.before {\n\t\t\tctx = f(ctx, msg)\n\t\t}\n\n\t\trequest, err := s.dec(ctx, msg)\n\t\tif err != nil {\n\t\t\ts.errorHandler.Handle(ctx, err)\n\t\t\tif msg.Reply == \"\" {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ts.errorEncoder(ctx, err, msg.Reply, nc)\n\t\t\treturn\n\t\t}\n\n\t\tresponse, err := s.e(ctx, request)\n\t\tif err != nil {\n\t\t\ts.errorHandler.Handle(ctx, err)\n\t\t\tif msg.Reply == \"\" {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ts.errorEncoder(ctx, err, msg.Reply, nc)\n\t\t\treturn\n\t\t}\n\n\t\tfor _, f := range s.after {\n\t\t\tctx = f(ctx, nc)\n\t\t}\n\n\t\tif msg.Reply == \"\" {\n\t\t\treturn\n\t\t}\n\n\t\tif err := s.enc(ctx, msg.Reply, nc, response); err != nil {\n\t\t\ts.errorHandler.Handle(ctx, err)\n\t\t\ts.errorEncoder(ctx, err, msg.Reply, nc)\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// ErrorEncoder is responsible for encoding an error to the subscriber reply.\n// Users are encouraged to use custom ErrorEncoders to encode errors to\n// their replies, and will likely want to pass and check for their own error\n// types.\ntype ErrorEncoder func(ctx context.Context, err error, reply string, nc *nats.Conn)\n\n// SubscriberFinalizerFunc can be used to perform work at the end of an request\n// from a publisher, after the response has been written to the publisher. The principal\n// intended use is for request logging.\ntype SubscriberFinalizerFunc func(ctx context.Context, msg *nats.Msg)\n\n// NopRequestDecoder is a DecodeRequestFunc that can be used for requests that do not\n// need to be decoded, and simply returns nil, nil.\nfunc NopRequestDecoder(_ context.Context, _ *nats.Msg) (interface{}, error) {\n\treturn nil, nil\n}\n\n// EncodeJSONResponse is a EncodeResponseFunc that serializes the response as a\n// JSON object to the subscriber reply. Many JSON-over services can use it as\n// a sensible default.\nfunc EncodeJSONResponse(_ context.Context, reply string, nc *nats.Conn, response interface{}) error {\n\tb, err := json.Marshal(response)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nc.Publish(reply, b)\n}\n\n// DefaultErrorEncoder writes the error to the subscriber reply.\nfunc DefaultErrorEncoder(_ context.Context, err error, reply string, nc *nats.Conn) {\n\tlogger := log.NewNopLogger()\n\n\ttype Response struct {\n\t\tError string `json:\"err\"`\n\t}\n\n\tvar response Response\n\n\tresponse.Error = err.Error()\n\n\tb, err := json.Marshal(response)\n\tif err != nil {\n\t\tlogger.Log(\"err\", err)\n\t\treturn\n\t}\n\n\tif err := nc.Publish(reply, b); err != nil {\n\t\tlogger.Log(\"err\", err)\n\t}\n}\n"
  },
  {
    "path": "transport/nats/subscriber_test.go",
    "content": "package nats_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/nats-io/nats-server/v2/server\"\n\t\"github.com/nats-io/nats.go\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n\tnatstransport \"github.com/go-kit/kit/transport/nats\"\n)\n\ntype TestResponse struct {\n\tString string `json:\"str\"`\n\tError  string `json:\"err\"`\n}\n\nfunc newNATSConn(t *testing.T) (*server.Server, *nats.Conn) {\n\ts, err := server.NewServer(&server.Options{\n\t\tHost: \"localhost\",\n\t\tPort: 0,\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tgo s.Start()\n\n\tfor i := 0; i < 5 && !s.Running(); i++ {\n\t\tt.Logf(\"Running %v\", s.Running())\n\t\ttime.Sleep(time.Second)\n\t}\n\tif !s.Running() {\n\t\ts.Shutdown()\n\t\ts.WaitForShutdown()\n\t\tt.Fatal(\"not yet running\")\n\t}\n\n\tif ok := s.ReadyForConnections(5 * time.Second); !ok {\n\t\tt.Fatal(\"not ready for connections\")\n\t}\n\n\tc, err := nats.Connect(\"nats://\"+s.Addr().String(), nats.Name(t.Name()))\n\tif err != nil {\n\t\tt.Fatalf(\"failed to connect to NATS server: %s\", err)\n\t}\n\n\treturn s, c\n}\n\nfunc TestSubscriberBadDecode(t *testing.T) {\n\ts, c := newNATSConn(t)\n\tdefer func() { s.Shutdown(); s.WaitForShutdown() }()\n\tdefer c.Close()\n\n\thandler := natstransport.NewSubscriber(\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil },\n\t\tfunc(context.Context, *nats.Msg) (interface{}, error) { return struct{}{}, errors.New(\"dang\") },\n\t\tfunc(context.Context, string, *nats.Conn, interface{}) error { return nil },\n\t)\n\n\tresp := testRequest(t, c, handler)\n\n\tif want, have := \"dang\", resp.Error; want != have {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t}\n\n}\n\nfunc TestSubscriberBadEndpoint(t *testing.T) {\n\ts, c := newNATSConn(t)\n\tdefer func() { s.Shutdown(); s.WaitForShutdown() }()\n\tdefer c.Close()\n\n\thandler := natstransport.NewSubscriber(\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return struct{}{}, errors.New(\"dang\") },\n\t\tfunc(context.Context, *nats.Msg) (interface{}, error) { return struct{}{}, nil },\n\t\tfunc(context.Context, string, *nats.Conn, interface{}) error { return nil },\n\t)\n\n\tresp := testRequest(t, c, handler)\n\n\tif want, have := \"dang\", resp.Error; want != have {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t}\n}\n\nfunc TestSubscriberBadEncode(t *testing.T) {\n\ts, c := newNATSConn(t)\n\tdefer func() { s.Shutdown(); s.WaitForShutdown() }()\n\tdefer c.Close()\n\n\thandler := natstransport.NewSubscriber(\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil },\n\t\tfunc(context.Context, *nats.Msg) (interface{}, error) { return struct{}{}, nil },\n\t\tfunc(context.Context, string, *nats.Conn, interface{}) error { return errors.New(\"dang\") },\n\t)\n\n\tresp := testRequest(t, c, handler)\n\n\tif want, have := \"dang\", resp.Error; want != have {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t}\n}\n\nfunc TestSubscriberErrorEncoder(t *testing.T) {\n\ts, c := newNATSConn(t)\n\tdefer func() { s.Shutdown(); s.WaitForShutdown() }()\n\tdefer c.Close()\n\n\terrTeapot := errors.New(\"teapot\")\n\tcode := func(err error) error {\n\t\tif errors.Is(err, errTeapot) {\n\t\t\treturn err\n\t\t}\n\t\treturn errors.New(\"dang\")\n\t}\n\thandler := natstransport.NewSubscriber(\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return struct{}{}, errTeapot },\n\t\tfunc(context.Context, *nats.Msg) (interface{}, error) { return struct{}{}, nil },\n\t\tfunc(context.Context, string, *nats.Conn, interface{}) error { return nil },\n\t\tnatstransport.SubscriberErrorEncoder(func(_ context.Context, err error, reply string, nc *nats.Conn) {\n\t\t\tvar r TestResponse\n\t\t\tr.Error = code(err).Error()\n\n\t\t\tb, err := json.Marshal(r)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\tif err := c.Publish(reply, b); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t}),\n\t)\n\n\tresp := testRequest(t, c, handler)\n\n\tif want, have := errTeapot.Error(), resp.Error; want != have {\n\t\tt.Errorf(\"want %s, have %s\", want, have)\n\t}\n}\n\nfunc TestSubscriberHappySubject(t *testing.T) {\n\tstep, response := testSubscriber(t)\n\tstep()\n\tr := <-response\n\n\tvar resp TestResponse\n\terr := json.Unmarshal(r.Data, &resp)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif want, have := \"\", resp.Error; want != have {\n\t\tt.Errorf(\"want %s, have %s (%s)\", want, have, r.Data)\n\t}\n}\n\nfunc TestMultipleSubscriberBefore(t *testing.T) {\n\ts, c := newNATSConn(t)\n\tdefer func() { s.Shutdown(); s.WaitForShutdown() }()\n\tdefer c.Close()\n\n\tvar (\n\t\tresponse = struct{ Body string }{\"go eat a fly ugly\\n\"}\n\t\twg       sync.WaitGroup\n\t\tdone     = make(chan struct{})\n\t)\n\thandler := natstransport.NewSubscriber(\n\t\tendpoint.Nop,\n\t\tfunc(context.Context, *nats.Msg) (interface{}, error) {\n\t\t\treturn struct{}{}, nil\n\t\t},\n\t\tfunc(_ context.Context, reply string, nc *nats.Conn, _ interface{}) error {\n\t\t\tb, err := json.Marshal(response)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\treturn c.Publish(reply, b)\n\t\t},\n\t\tnatstransport.SubscriberBefore(func(ctx context.Context, _ *nats.Msg) context.Context {\n\t\t\tctx = context.WithValue(ctx, \"one\", 1)\n\n\t\t\treturn ctx\n\t\t}),\n\t\tnatstransport.SubscriberBefore(func(ctx context.Context, _ *nats.Msg) context.Context {\n\t\t\tif _, ok := ctx.Value(\"one\").(int); !ok {\n\t\t\t\tt.Error(\"Value was not set properly when multiple ServerBefores are used\")\n\t\t\t}\n\n\t\t\tclose(done)\n\t\t\treturn ctx\n\t\t}),\n\t)\n\n\tsub, err := c.QueueSubscribe(\"natstransport.test\", \"natstransport\", handler.ServeMsg(c))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer sub.Unsubscribe()\n\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\t_, err := c.Request(\"natstransport.test\", []byte(\"test data\"), 2*time.Second)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t}()\n\n\tselect {\n\tcase <-done:\n\tcase <-time.After(time.Second):\n\t\tt.Fatal(\"timeout waiting for finalizer\")\n\t}\n\n\twg.Wait()\n}\n\nfunc TestMultipleSubscriberAfter(t *testing.T) {\n\ts, c := newNATSConn(t)\n\tdefer func() { s.Shutdown(); s.WaitForShutdown() }()\n\tdefer c.Close()\n\n\tvar (\n\t\tresponse = struct{ Body string }{\"go eat a fly ugly\\n\"}\n\t\twg       sync.WaitGroup\n\t\tdone     = make(chan struct{})\n\t)\n\thandler := natstransport.NewSubscriber(\n\t\tendpoint.Nop,\n\t\tfunc(context.Context, *nats.Msg) (interface{}, error) {\n\t\t\treturn struct{}{}, nil\n\t\t},\n\t\tfunc(_ context.Context, reply string, nc *nats.Conn, _ interface{}) error {\n\t\t\tb, err := json.Marshal(response)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn c.Publish(reply, b)\n\t\t},\n\t\tnatstransport.SubscriberAfter(func(ctx context.Context, nc *nats.Conn) context.Context {\n\t\t\treturn context.WithValue(ctx, \"one\", 1)\n\t\t}),\n\t\tnatstransport.SubscriberAfter(func(ctx context.Context, nc *nats.Conn) context.Context {\n\t\t\tif _, ok := ctx.Value(\"one\").(int); !ok {\n\t\t\t\tt.Error(\"Value was not set properly when multiple ServerAfters are used\")\n\t\t\t}\n\t\t\tclose(done)\n\t\t\treturn ctx\n\t\t}),\n\t)\n\n\tsub, err := c.QueueSubscribe(\"natstransport.test\", \"natstransport\", handler.ServeMsg(c))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer sub.Unsubscribe()\n\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\t_, err := c.Request(\"natstransport.test\", []byte(\"test data\"), 2*time.Second)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t}()\n\n\tselect {\n\tcase <-done:\n\tcase <-time.After(time.Second):\n\t\tt.Fatal(\"timeout waiting for finalizer\")\n\t}\n\n\twg.Wait()\n}\n\nfunc TestSubscriberFinalizerFunc(t *testing.T) {\n\ts, c := newNATSConn(t)\n\tdefer func() { s.Shutdown(); s.WaitForShutdown() }()\n\tdefer c.Close()\n\n\tvar (\n\t\tresponse = struct{ Body string }{\"go eat a fly ugly\\n\"}\n\t\twg       sync.WaitGroup\n\t\tdone     = make(chan struct{})\n\t)\n\thandler := natstransport.NewSubscriber(\n\t\tendpoint.Nop,\n\t\tfunc(context.Context, *nats.Msg) (interface{}, error) {\n\t\t\treturn struct{}{}, nil\n\t\t},\n\t\tfunc(_ context.Context, reply string, nc *nats.Conn, _ interface{}) error {\n\t\t\tb, err := json.Marshal(response)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\treturn c.Publish(reply, b)\n\t\t},\n\t\tnatstransport.SubscriberFinalizer(func(ctx context.Context, _ *nats.Msg) {\n\t\t\tclose(done)\n\t\t}),\n\t)\n\n\tsub, err := c.QueueSubscribe(\"natstransport.test\", \"natstransport\", handler.ServeMsg(c))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer sub.Unsubscribe()\n\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\t_, err := c.Request(\"natstransport.test\", []byte(\"test data\"), 2*time.Second)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t}()\n\n\tselect {\n\tcase <-done:\n\tcase <-time.After(time.Second):\n\t\tt.Fatal(\"timeout waiting for finalizer\")\n\t}\n\n\twg.Wait()\n}\n\nfunc TestEncodeJSONResponse(t *testing.T) {\n\ts, c := newNATSConn(t)\n\tdefer func() { s.Shutdown(); s.WaitForShutdown() }()\n\tdefer c.Close()\n\n\thandler := natstransport.NewSubscriber(\n\t\tfunc(context.Context, interface{}) (interface{}, error) {\n\t\t\treturn struct {\n\t\t\t\tFoo string `json:\"foo\"`\n\t\t\t}{\"bar\"}, nil\n\t\t},\n\t\tfunc(context.Context, *nats.Msg) (interface{}, error) { return struct{}{}, nil },\n\t\tnatstransport.EncodeJSONResponse,\n\t)\n\n\tsub, err := c.QueueSubscribe(\"natstransport.test\", \"natstransport\", handler.ServeMsg(c))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer sub.Unsubscribe()\n\n\tr, err := c.Request(\"natstransport.test\", []byte(\"test data\"), 2*time.Second)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif want, have := `{\"foo\":\"bar\"}`, strings.TrimSpace(string(r.Data)); want != have {\n\t\tt.Errorf(\"Body: want %s, have %s\", want, have)\n\t}\n}\n\ntype responseError struct {\n\tmsg string\n}\n\nfunc (m responseError) Error() string {\n\treturn m.msg\n}\n\nfunc TestErrorEncoder(t *testing.T) {\n\ts, c := newNATSConn(t)\n\tdefer func() { s.Shutdown(); s.WaitForShutdown() }()\n\tdefer c.Close()\n\n\terrResp := struct {\n\t\tError string `json:\"err\"`\n\t}{\"oh no\"}\n\thandler := natstransport.NewSubscriber(\n\t\tfunc(context.Context, interface{}) (interface{}, error) {\n\t\t\treturn nil, responseError{msg: errResp.Error}\n\t\t},\n\t\tfunc(context.Context, *nats.Msg) (interface{}, error) { return struct{}{}, nil },\n\t\tnatstransport.EncodeJSONResponse,\n\t)\n\n\tsub, err := c.QueueSubscribe(\"natstransport.test\", \"natstransport\", handler.ServeMsg(c))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer sub.Unsubscribe()\n\n\tr, err := c.Request(\"natstransport.test\", []byte(\"test data\"), 2*time.Second)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tb, err := json.Marshal(errResp)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif string(b) != string(r.Data) {\n\t\tt.Errorf(\"ErrorEncoder: got: %q, expected: %q\", r.Data, b)\n\t}\n}\n\ntype noContentResponse struct{}\n\nfunc TestEncodeNoContent(t *testing.T) {\n\ts, c := newNATSConn(t)\n\tdefer func() { s.Shutdown(); s.WaitForShutdown() }()\n\tdefer c.Close()\n\n\thandler := natstransport.NewSubscriber(\n\t\tfunc(context.Context, interface{}) (interface{}, error) { return noContentResponse{}, nil },\n\t\tfunc(context.Context, *nats.Msg) (interface{}, error) { return struct{}{}, nil },\n\t\tnatstransport.EncodeJSONResponse,\n\t)\n\n\tsub, err := c.QueueSubscribe(\"natstransport.test\", \"natstransport\", handler.ServeMsg(c))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer sub.Unsubscribe()\n\n\tr, err := c.Request(\"natstransport.test\", []byte(\"test data\"), 2*time.Second)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif want, have := `{}`, strings.TrimSpace(string(r.Data)); want != have {\n\t\tt.Errorf(\"Body: want %s, have %s\", want, have)\n\t}\n}\n\nfunc TestNoOpRequestDecoder(t *testing.T) {\n\ts, c := newNATSConn(t)\n\tdefer func() { s.Shutdown(); s.WaitForShutdown() }()\n\tdefer c.Close()\n\n\thandler := natstransport.NewSubscriber(\n\t\tfunc(ctx context.Context, request interface{}) (interface{}, error) {\n\t\t\tif request != nil {\n\t\t\t\tt.Error(\"Expected nil request in endpoint when using NopRequestDecoder\")\n\t\t\t}\n\t\t\treturn nil, nil\n\t\t},\n\t\tnatstransport.NopRequestDecoder,\n\t\tnatstransport.EncodeJSONResponse,\n\t)\n\n\tsub, err := c.QueueSubscribe(\"natstransport.test\", \"natstransport\", handler.ServeMsg(c))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer sub.Unsubscribe()\n\n\tr, err := c.Request(\"natstransport.test\", []byte(\"test data\"), 2*time.Second)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif want, have := `null`, strings.TrimSpace(string(r.Data)); want != have {\n\t\tt.Errorf(\"Body: want %s, have %s\", want, have)\n\t}\n}\n\nfunc testSubscriber(t *testing.T) (step func(), resp <-chan *nats.Msg) {\n\tvar (\n\t\tstepch   = make(chan bool)\n\t\tendpoint = func(context.Context, interface{}) (interface{}, error) {\n\t\t\t<-stepch\n\t\t\treturn struct{}{}, nil\n\t\t}\n\t\tresponse = make(chan *nats.Msg)\n\t\thandler  = natstransport.NewSubscriber(\n\t\t\tendpoint,\n\t\t\tfunc(context.Context, *nats.Msg) (interface{}, error) { return struct{}{}, nil },\n\t\t\tnatstransport.EncodeJSONResponse,\n\t\t\tnatstransport.SubscriberBefore(func(ctx context.Context, msg *nats.Msg) context.Context { return ctx }),\n\t\t\tnatstransport.SubscriberAfter(func(ctx context.Context, nc *nats.Conn) context.Context { return ctx }),\n\t\t)\n\t)\n\n\tgo func() {\n\t\ts, c := newNATSConn(t)\n\t\tdefer func() { s.Shutdown(); s.WaitForShutdown() }()\n\t\tdefer c.Close()\n\n\t\tsub, err := c.QueueSubscribe(\"natstransport.test\", \"natstransport\", handler.ServeMsg(c))\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tdefer sub.Unsubscribe()\n\n\t\tr, err := c.Request(\"natstransport.test\", []byte(\"test data\"), 2*time.Second)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tresponse <- r\n\t}()\n\n\treturn func() { stepch <- true }, response\n}\n\nfunc testRequest(t *testing.T, c *nats.Conn, handler *natstransport.Subscriber) TestResponse {\n\tsub, err := c.QueueSubscribe(\"natstransport.test\", \"natstransport\", handler.ServeMsg(c))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer sub.Unsubscribe()\n\n\tr, err := c.Request(\"natstransport.test\", []byte(\"test data\"), 2*time.Second)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar resp TestResponse\n\terr = json.Unmarshal(r.Data, &resp)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\treturn resp\n}\n"
  },
  {
    "path": "transport/netrpc/README.md",
    "content": "# net/rpc\n\n[net/rpc](https://golang.org/pkg/net/rpc) is an RPC transport that's part of the Go standard library.\nIt's a simple and fast transport that's appropriate when all of your services are written in Go.\n\nUsing net/rpc with Go kit is very simple.\nJust write a simple binding from your service definition to the net/rpc definition.\nSee [netrpc_binding.go](https://github.com/go-kit/kit/blob/ec8b02591ee873433565a1ae9d317353412d1d27/examples/addsvc/netrpc_binding.go) for an example.\n\nThat's it!\nThe net/rpc binding can be registered to a name, and bound to an HTTP handler, the same as any other net/rpc endpoint.\nAnd within your service, you can use standard Go kit components and idioms.\nSee [addsvc](https://github.com/go-kit/examples/tree/master/addsvc) for a complete working example with net/rpc support.\nAnd remember: Go kit services can support multiple transports simultaneously.\n"
  },
  {
    "path": "transport/thrift/README.md",
    "content": "# Thrift\n\n[Thrift](https://thrift.apache.org/) is a large IDL and transport package from Apache, popularized by Facebook.\nThrift is well-supported in Go kit, for organizations that already have significant Thrift investment.\nAnd using Thrift with Go kit is very simple.\n\nFirst, define your service in the Thrift IDL.\nThe [Thrift IDL documentation](https://thrift.apache.org/docs/idl) provides more details.\nSee [addsvc.thrift](https://github.com/go-kit/examples/blob/master/addsvc/thrift/addsvc.thrift) for an example.\nMake sure the Thrift definition matches your service's Go kit (interface) definition.\n\nNext, [download Thrift](https://thrift.apache.org/download) and [install the compiler](https://thrift.apache.org/docs/install/).\nOn a Mac, you may be able to `brew install thrift`.\n\nThen, compile your service definition, from .thrift to .go.\nYou'll probably want to specify the package_prefix option to the --gen go flag.\nSee [THRIFT-3021](https://issues.apache.org/jira/browse/THRIFT-3021) for more details.\n\n```\nthrift -r --gen go:package_prefix=github.com/my-org/my-repo/thrift/gen-go/ add.thrift\n```\n\nFinally, write a tiny binding from your service definition to the Thrift definition.\nIt's a straightforward conversion from one domain to the other.\nSee [thrift.go](https://github.com/go-kit/examples/blob/master/addsvc/pkg/addtransport/thrift.go) for an example.\n\nThat's it!\nThe Thrift binding can be bound to a listener and serve normal Thrift requests.\nAnd within your service, you can use standard Go kit components and idioms.\nUnfortunately, setting up a Thrift listener is rather laborious and nonidiomatic in Go.\nFortunately, [addsvc](https://github.com/go-kit/examples/tree/master/addsvc) is a complete working example with Thrift support.\nAnd remember: Go kit services can support multiple transports simultaneously.\n"
  },
  {
    "path": "util/README.md",
    "content": "# util\n\nThis directory holds packages of general utility to multiple consumers within Go kit,\n and potentially other consumers in the wider Go ecosystem.\nThere is no `package util` and will never be.\n"
  },
  {
    "path": "util/conn/doc.go",
    "content": "// Package conn provides utilities related to connections.\npackage conn\n"
  },
  {
    "path": "util/conn/manager.go",
    "content": "package conn\n\nimport (\n\t\"errors\"\n\t\"math/rand\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/go-kit/log\"\n)\n\n// Dialer imitates net.Dial. Dialer is assumed to yield connections that are\n// safe for use by multiple concurrent goroutines.\ntype Dialer func(network, address string) (net.Conn, error)\n\n// AfterFunc imitates time.After.\ntype AfterFunc func(time.Duration) <-chan time.Time\n\n// Manager manages a net.Conn.\n//\n// Clients provide a way to create the connection with a Dialer, network, and\n// address. Clients should Take the connection when they want to use it, and Put\n// back whatever error they receive from its use. When a non-nil error is Put,\n// the connection is invalidated, and a new connection is established.\n// Connection failures are retried after an exponential backoff.\ntype Manager struct {\n\tdialer  Dialer\n\tnetwork string\n\taddress string\n\tafter   AfterFunc\n\tlogger  log.Logger\n\n\ttakec chan net.Conn\n\tputc  chan error\n}\n\n// NewManager returns a connection manager using the passed Dialer, network, and\n// address. The AfterFunc is used to control exponential backoff and retries.\n// The logger is used to log errors; pass a log.NopLogger if you don't care to\n// receive them. For normal use, prefer NewDefaultManager.\nfunc NewManager(d Dialer, network, address string, after AfterFunc, logger log.Logger) *Manager {\n\tm := &Manager{\n\t\tdialer:  d,\n\t\tnetwork: network,\n\t\taddress: address,\n\t\tafter:   after,\n\t\tlogger:  logger,\n\n\t\ttakec: make(chan net.Conn),\n\t\tputc:  make(chan error),\n\t}\n\tgo m.loop()\n\treturn m\n}\n\n// NewDefaultManager is a helper constructor, suitable for most normal use in\n// real (non-test) code. It uses the real net.Dial and time.After functions.\nfunc NewDefaultManager(network, address string, logger log.Logger) *Manager {\n\treturn NewManager(net.Dial, network, address, time.After, logger)\n}\n\n// Take yields the current connection. It may be nil.\nfunc (m *Manager) Take() net.Conn {\n\treturn <-m.takec\n}\n\n// Put accepts an error that came from a previously yielded connection. If the\n// error is non-nil, the manager will invalidate the current connection and try\n// to reconnect, with exponential backoff. Putting a nil error is a no-op.\nfunc (m *Manager) Put(err error) {\n\tm.putc <- err\n}\n\n// Write writes the passed data to the connection in a single Take/Put cycle.\nfunc (m *Manager) Write(b []byte) (int, error) {\n\tconn := m.Take()\n\tif conn == nil {\n\t\treturn 0, ErrConnectionUnavailable\n\t}\n\tn, err := conn.Write(b)\n\tdefer m.Put(err)\n\treturn n, err\n}\n\nfunc (m *Manager) loop() {\n\tvar (\n\t\tconn       = dial(m.dialer, m.network, m.address, m.logger) // may block slightly\n\t\tconnc      = make(chan net.Conn, 1)\n\t\treconnectc <-chan time.Time // initially nil\n\t\tbackoff    = time.Second\n\t)\n\n\t// If the initial dial fails, we need to trigger a reconnect via the loop\n\t// body, below. If we did this in a goroutine, we would race on the conn\n\t// variable. So we use a buffered chan instead.\n\tconnc <- conn\n\n\tfor {\n\t\tselect {\n\t\tcase <-reconnectc:\n\t\t\treconnectc = nil // one-shot\n\t\t\tgo func() { connc <- dial(m.dialer, m.network, m.address, m.logger) }()\n\n\t\tcase conn = <-connc:\n\t\t\tif conn == nil {\n\t\t\t\t// didn't work\n\t\t\t\tbackoff = Exponential(backoff) // wait longer\n\t\t\t\treconnectc = m.after(backoff)  // try again\n\t\t\t} else {\n\t\t\t\t// worked!\n\t\t\t\tbackoff = time.Second // reset wait time\n\t\t\t\treconnectc = nil      // no retry necessary\n\t\t\t}\n\n\t\tcase m.takec <- conn:\n\n\t\tcase err := <-m.putc:\n\t\t\tif err != nil && conn != nil {\n\t\t\t\tm.logger.Log(\"err\", err)\n\t\t\t\tconn.Close()\n\t\t\t\tconn = nil                            // connection is bad\n\t\t\t\treconnectc = m.after(time.Nanosecond) // trigger immediately\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc dial(d Dialer, network, address string, logger log.Logger) net.Conn {\n\tconn, err := d(network, address)\n\tif err != nil {\n\t\tlogger.Log(\"err\", err)\n\t\tconn = nil // just to be sure\n\t}\n\treturn conn\n}\n\n// Exponential takes a duration and returns another one that is twice as long, +/- 50%. It is\n// used to provide backoff for operations that may fail and should avoid thundering herds.\n// See https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ for rationale\nfunc Exponential(d time.Duration) time.Duration {\n\td *= 2\n\tjitter := rand.Float64() + 0.5\n\td = time.Duration(int64(float64(d.Nanoseconds()) * jitter))\n\tif d > time.Minute {\n\t\td = time.Minute\n\t}\n\treturn d\n\n}\n\n// ErrConnectionUnavailable is returned by the Manager's Write method when the\n// manager cannot yield a good connection.\nvar ErrConnectionUnavailable = errors.New(\"connection unavailable\")\n"
  },
  {
    "path": "util/conn/manager_test.go",
    "content": "package conn\n\nimport (\n\t\"errors\"\n\t\"net\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-kit/log\"\n)\n\nfunc TestManager(t *testing.T) {\n\tvar (\n\t\ttickc    = make(chan time.Time)\n\t\tafter    = func(time.Duration) <-chan time.Time { return tickc }\n\t\tdialconn = &mockConn{}\n\t\tdialerr  = error(nil)\n\t\tdialer   = func(string, string) (net.Conn, error) { return dialconn, dialerr }\n\t\tmgr      = NewManager(dialer, \"netw\", \"addr\", after, log.NewNopLogger())\n\t)\n\n\t// First conn should be fine.\n\tconn := mgr.Take()\n\tif conn == nil {\n\t\tt.Fatal(\"nil conn\")\n\t}\n\n\t// Write and check it went through.\n\tif _, err := conn.Write([]byte{1, 2, 3}); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif want, have := uint64(3), atomic.LoadUint64(&dialconn.wr); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\t// Put an error to kill the conn.\n\tmgr.Put(errors.New(\"should kill the connection\"))\n\n\t// First takes should fail.\n\tfor i := 0; i < 10; i++ {\n\t\tif conn = mgr.Take(); conn != nil {\n\t\t\tt.Fatalf(\"iteration %d: want nil conn, got real conn\", i)\n\t\t}\n\t}\n\n\t// Trigger the reconnect.\n\ttickc <- time.Now()\n\n\t// The dial should eventually succeed and yield a good conn.\n\tif !within(100*time.Millisecond, func() bool {\n\t\tconn = mgr.Take()\n\t\treturn conn != nil\n\t}) {\n\t\tt.Fatal(\"conn remained nil\")\n\t}\n\n\t// Write and check it went through.\n\tif _, err := conn.Write([]byte{4, 5}); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif want, have := uint64(5), atomic.LoadUint64(&dialconn.wr); want != have {\n\t\tt.Errorf(\"want %d, have %d\", want, have)\n\t}\n\n\t// Dial starts failing.\n\tdialconn, dialerr = nil, errors.New(\"oh noes\")\n\tmgr.Put(errors.New(\"trigger that reconnect y'all\"))\n\tif conn = mgr.Take(); conn != nil {\n\t\tt.Fatalf(\"want nil conn, got real conn\")\n\t}\n\n\t// As many reconnects as they want.\n\tgo func() {\n\t\tdone := time.After(100 * time.Millisecond)\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase tickc <- time.Now():\n\t\t\tcase <-done:\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\n\t// The dial should never succeed.\n\tif within(100*time.Millisecond, func() bool {\n\t\tconn = mgr.Take()\n\t\treturn conn != nil\n\t}) {\n\t\tt.Fatal(\"eventually got a good conn, despite failing dialer\")\n\t}\n}\n\nfunc TestIssue292(t *testing.T) {\n\t// The util/conn.Manager won't attempt to reconnect to the provided endpoint\n\t// if the endpoint is initially unavailable (e.g. dial tcp :8080:\n\t// getsockopt: connection refused). If the endpoint is up when\n\t// conn.NewManager is called and then goes down/up, it reconnects just fine.\n\n\tvar (\n\t\ttickc    = make(chan time.Time)\n\t\tafter    = func(time.Duration) <-chan time.Time { return tickc }\n\t\tdialconn = net.Conn(nil)\n\t\tdialerr  = errors.New(\"fail\")\n\t\tdialer   = func(string, string) (net.Conn, error) { return dialconn, dialerr }\n\t\tmgr      = NewManager(dialer, \"netw\", \"addr\", after, log.NewNopLogger())\n\t)\n\n\tif conn := mgr.Take(); conn != nil {\n\t\tt.Fatal(\"first Take should have yielded nil conn, but didn't\")\n\t}\n\n\tdialconn, dialerr = &mockConn{}, nil\n\tselect {\n\tcase tickc <- time.Now():\n\tcase <-time.After(time.Second):\n\t\tt.Fatal(\"manager isn't listening for a tick, despite a failed dial\")\n\t}\n\n\tif !within(time.Second, func() bool {\n\t\treturn mgr.Take() != nil\n\t}) {\n\t\tt.Fatal(\"second Take should have yielded good conn, but didn't\")\n\t}\n}\n\ntype mockConn struct {\n\trd, wr uint64\n}\n\nfunc (c *mockConn) Read(b []byte) (n int, err error) {\n\tatomic.AddUint64(&c.rd, uint64(len(b)))\n\treturn len(b), nil\n}\n\nfunc (c *mockConn) Write(b []byte) (n int, err error) {\n\tatomic.AddUint64(&c.wr, uint64(len(b)))\n\treturn len(b), nil\n}\n\nfunc (c *mockConn) Close() error                       { return nil }\nfunc (c *mockConn) LocalAddr() net.Addr                { return nil }\nfunc (c *mockConn) RemoteAddr() net.Addr               { return nil }\nfunc (c *mockConn) SetDeadline(t time.Time) error      { return nil }\nfunc (c *mockConn) SetReadDeadline(t time.Time) error  { return nil }\nfunc (c *mockConn) SetWriteDeadline(t time.Time) error { return nil }\n\nfunc within(d time.Duration, f func() bool) bool {\n\tdeadline := time.Now().Add(d)\n\tfor {\n\t\tif time.Now().After(deadline) {\n\t\t\treturn false\n\t\t}\n\t\tif f() {\n\t\t\treturn true\n\t\t}\n\t\ttime.Sleep(d / 10)\n\t}\n}\n"
  }
]