[
  {
    "path": ".gitignore",
    "content": ".idea\nvenv\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Packt\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"
  },
  {
    "path": "README.md",
    "content": "\n\n\n# Go for DevOps\n\n<a href=\"https://www.packtpub.com/product/go-for-devops/9781801818896?utm_source=github&utm_medium=repository&utm_campaign=9781801818896\"><img src=\"https://static.packt-cdn.com/products/9781801818896/cover/smaller\" alt=\"Go for DevOps\" height=\"256px\" align=\"right\"></a>\n\nThis is the code repository for [Go for DevOps](https://www.packtpub.com/product/go-for-devops/9781801818896?utm_source=github&utm_medium=repository&utm_campaign=9781801818896), published by Packt.\n\n**Learn how to use the Go language to automate servers, the cloud, Kubernetes, GitHub, Packer, and Terraform**\n\n## What is this book about?\nGo is the go-to language for DevOps libraries and services, and without it, achieving fast and safe automation is a challenge. With the help of Go for DevOps, you'll learn how to deliver services with ease and safety, becoming a better DevOps engineer in the process. \n\nThis book covers the following exciting features:\n* Understand the basic structure of the Go language to begin your DevOps journey\n* Interact with filesystems to read or stream data\n* Communicate with remote services via REST and gRPC\n* Explore writing tools that can be used in the DevOps environment\n* Develop command-line operational software in Go\n* Work with popular frameworks to deploy production software\n* Create GitHub actions that streamline your CI/CD process\n* Write a ChatOps application with Slack to simplify production visibility\n\nIf you feel this book is for you, get your [copy](https://www.amazon.com/dp/1801818894) today!\n\n<a href=\"https://www.packtpub.com/?utm_source=github&utm_medium=banner&utm_campaign=GitHubBanner\"><img src=\"https://raw.githubusercontent.com/PacktPublishing/GitHub/master/GitHub.png\" \nalt=\"https://www.packtpub.com/\" border=\"5\" /></a>\n\n## Instructions and Navigations\nAll of the code is organized into folders. For example, Chapter02.\n\nThe code will look like the following:\n```\npacker {\n required_plugins {\n amazon = {\n version = \">= 0.0.1\"\n\n```\n\n**Following is what you need for this book:**\nThis book is for Ops and DevOps engineers who would like to use Go to develop their own DevOps tooling or integrate custom features with DevOps tools such as Kubernetes, GitHub Actions, HashiCorp Packer, and Terraform. Experience with some type of programming language, but not necessarily Go, is necessary to get started with this book.\n\nWith the following software and hardware list you can run all code files present in the book (Chapter 1-16).\n### Software and Hardware List\n| Chapter  | Software required | OS required |\n| -------- | ------------------------------------ | ----------------------------------- |\n| 1-16     | Go 1.18           | Windows, Mac OS X, and Linux (Any) |\n| 1-16     | Packer            | Windows, Mac OS X, and Linux (Any) |\n| 1-16     | Terraform         | Windows, Mac OS X, and Linux (Any) |\n| 1-16     | Kubernetes        | Windows, Mac OS X, and Linux (Any) |\n| 1-16     | Docker            | Windows, Mac OS X, and Linux (Any) |\n| 1-16     | Tilt              | Windows, Mac OS X, and Linux (Any) |\n| 1-16     | Protocol Buffers  | Windows, Mac OS X, and Linux (Any) |\n| 1-16     | gPRC,ctlptl       | Windows, Mac OS X, and Linux (Any) |\n| 1-16     | But CLI           | Windows, Mac OS X, and Linux (Any) |\n| 1-16     | Operator SDK      | Windows, Mac OS X, and Linux (Any) |\n| 1-16     | Azure CLI, KinD   | Windows, Mac OS X, and Linux (Any) |\n\nWe also provide a PDF file that has color images of the screenshots/diagrams used in this book. [Click here to download it](https://static.packt-cdn.com/downloads/9781801818896_ColorImages.pdf).\n\n### Related products\n* Learning DevOps - Second Edition [[Packt]](https://www.packtpub.com/product/learning-devops-second-edition/9781801818964?utm_source=github&utm_medium=repository&utm_campaign=9781801818964) [[Amazon]](https://www.amazon.com/dp/1801818967)\n\n* The DevOps Career Handbook [[Packt]](https://www.packtpub.com/product/the-devops-career-handbook/9781803230948?utm_source=github&utm_medium=repository&utm_campaign=9781803230948) [[Amazon]](https://www.amazon.com/dp/1803230940)\n\n## Errata\n * Page xxi (Under to get the most out of this book): **gPRC(https://grpc.io)** _should be_ **gRPC(https://grpc.io)**\n * Page 29 (Under Returning multiple values and named results): **func divide(num, div int) (res, rem int) { result = num / div remainder = num % div return res, rem }** _should be_ **func divide(num, div int) (res, rem int) {res = num / div rem = num % div return res, rem\n}**\n * Page 74, Third paragraph: **we will spin off 10 goroutines to add a number to a sum value** _should be_ **we will spin off 100 goroutines to add a number to a sum value**\n * Page 77, Code snippet: `if ctx.Err() != nil { return nil, err }` _should be_ ` if err := ctx.Err() != nil { return nil, err }`\n\n## Get to Know the Authors\n**John Doak**\nis the principal manager of Layer 1 Reliability Engineering at Microsoft. John led the development of the Azure Data Explorer and Microsoft Authentication Library Go SDKs. Previously, he was a Staff Site Reliability Engineer at Google. As part of network engineering, he created many of their first network automation systems. John led the migration of that group from Python to Go, developing Go training classes that have been taught around the world. He was a pivotal figure in transforming the network team to a network/systems group that integrated with SRE. Prior to that, he worked for Lucasfilm in video games and film. You can find his musings on Go/SRE topics and his Go classes on the web.\n\n**David Justice**\nis the principal software engineer lead for the Azure K8s infrastructure and Steel Thread teams, which maintain a variety of CNCF and Bytecode Alliance projects. He is a maintainer of the Cluster API Provider Azure and a contributor to the Cluster API. Prior to that, David was the technical assistant to the Azure CTO, where he was responsible for Azure cross-group technical strategy and architecture. Early on at Microsoft, he was a program manager leading Azure SDKs and CLIs, where he transitioned all Azure services to describe them using OpenAPI specifications in GitHub and established automations to generate Azure reference docs, SDKs, and CLIs. Prior to working at Microsoft, David was the CTO of a mobile CI/CD SaaS called CISimple.\n### Download a free PDF\n\n <i>If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.<br>Simply click on the link to claim your free PDF.</i>\n<p align=\"center\"> <a href=\"https://packt.link/free-ebook/9781801818896\">https://packt.link/free-ebook/9781801818896 </a> </p>\n"
  },
  {
    "path": "chapter/10/.github/release.yml",
    "content": "changelog:\n  exclude:\n    labels:\n      - ignore-for-release\n  categories:\n    - title: Breaking Changes 🛠\n      labels:\n        - breaking-change\n    - title: New Features 🎉\n      labels:\n        - enhancement\n    - title: Bug Fixes 🐛\n      labels:\n        - bug-fix\n    - title: Other Changes\n      labels:\n        - \"*\"\n"
  },
  {
    "path": "chapter/10/.github/workflows/action-version.yaml",
    "content": "name: Release new tweeter version\non:\n  release:\n    types: [released]\n  workflow_dispatch:\n    inputs:\n      TAG_NAME:\n        description: 'Tag name that the major tag will point to'\n        required: true\n\npermissions:\n  contents: write\n\nenv:\n  TAG_NAME: ${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }}\n\njobs:\n  update_tag:\n    name: Update the major tag to include the ${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }} changes\n    runs-on: ubuntu-latest\n    steps:\n      - name: Update the ${{ env.TAG_NAME }} tag\n        uses: actions/publish-action@v0.1.0\n        with:\n          source-tag: ${{ env.TAG_NAME }}\n"
  },
  {
    "path": "chapter/10/.github/workflows/first.yaml",
    "content": "name: first-workflow\non: workflow_dispatch\njobs:\n  echo:\n    runs-on: ubuntu-latest\n    steps:\n      - name: echo step\n        run: echo 'Hello World!'\n"
  },
  {
    "path": "chapter/10/.github/workflows/image-release.yaml",
    "content": "name: release image\non:\n  push:\n    tags:\n      - 'image-v*' # push events for tags matching image-v for version (image-v1.0, etc)\npermissions:\n  contents: read\n  packages: write\njobs:\n  image:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - name: set env\n        run: echo \"RELEASE_VERSION=${GITHUB_REF:17}\" >> $GITHUB_ENV # refs/tags/image-v1.0.0 substring starting at 1.0.0\n      - name: setup buildx\n        uses: docker/setup-buildx-action@v1\n      - name: login to GitHub container registry\n        uses: docker/login-action@v1\n        with:\n          registry: ghcr.io\n          username: ${{ github.repository_owner }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n      - name: build and push\n        uses: docker/build-push-action@v2\n        with:\n          push: true\n          tags: |\n            ghcr.io/PacktPublishing/tweeter:${{ env.RELEASE_VERSION }}\n            ghcr.io/PacktPublishing/tweeter:latest\n"
  },
  {
    "path": "chapter/10/.github/workflows/tweeter-automation.yaml",
    "content": "name: tweeter-automation\non:\n  push:\n    tags:\n      - 'v[0-9]+.[0-9]+.*'\n    branches:\n      - main\n  pull_request:\n    branches:\n      - main\njobs:\n  test:\n    permissions:\n      contents: read\n    strategy:\n      matrix:\n        go-version: [ 1.16.x, 1.17.x ]\n        os: [ ubuntu-latest, macos-latest, windows-latest ]\n    runs-on: ${{ matrix.os }}\n    steps:\n      - name: install go\n        uses: actions/setup-go@v2\n        with:\n          go-version: ${{ matrix.go-version }}\n      - uses: actions/checkout@v2\n      - name: lint with golangci-lint\n        uses: golangci/golangci-lint-action@v2\n      - name: run go test\n        run: go test ./...\n  test-action:\n    permissions:\n      contents: read\n      packages: read\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      # TODO: when the repo is open sourced, remove the login and open the image to the public\n      - name: login to GitHub container registry\n        uses: docker/login-action@v1\n        with:\n          registry: ghcr.io\n          username: ${{ github.repository_owner }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n      - run: docker pull ghcr.io/PacktPublishing/tweeter:1.0.0\n      - name: test the tweeter action in DRY_RUN\n        id: tweeterAction\n        env:\n          DRY_RUN: true\n        uses: ./\n        with:\n          message: hello world!\n          accessToken: fake\n          accessTokenSecret: fake\n          apiKey: fake\n          apiKeySecret: fake\n      - run: echo ${{ steps.tweeterAction.outputs.sentMessage }} from dry run test\n  release:\n    permissions:\n      contents: write\n    needs: test\n    if: startsWith(github.ref, 'refs/tags/v')\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - name: Set RELEASE_VERSION ENV var\n        run: echo \"RELEASE_VERSION=${GITHUB_REF:10}\" >> $GITHUB_ENV\n      - uses: actions/setup-go@v2\n        with:\n          go-version: 1.17.x\n      - name: install gox\n        run: go install github.com/mitchellh/gox@v1.0.1\n      - name: build cross-platform binaries\n        env:\n          PLATFORMS: darwin/amd64 darwin/arm64 windows/amd64 linux/amd64 linux/arm64\n          VERSION_INJECT: github.com/PacktPublishing/Go-for-DevOps/chapter/10/pkg/tweeter.Version\n          OUTPUT_PATH_FORMAT: ./bin/${{ env.RELEASE_VERSION }}/{{.OS}}/{{.Arch}}/tweeter\n        run: |\n          gox -osarch=\"${PLATFORMS}\" -ldflags \"-X ${VERSION_INJECT}=${RELEASE_VERSION}\" -output \"${OUTPUT_PATH_FORMAT}\"\n      - name: generate release notes\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: |\n          gh api -X POST 'repos/{owner}/{repo}/releases/generate-notes' \\\n            -F commitish=${{ env.RELEASE_VERSION }} \\\n            -F tag_name=${{ env.RELEASE_VERSION }} \\\n            > tmp-release-notes.json\n      - name: gzip the bins\n        env:\n          OUT_BASE: ./bin/${{ env.RELEASE_VERSION }}\n        run: |\n          tar -czvf \"${OUT_BASE}/darwin/amd64/tweeter_darwin_amd64.tar.gz\" -C \"${OUT_BASE}/darwin/amd64\" tweeter\n          tar -czvf \"${OUT_BASE}/darwin/arm64/tweeter_darwin_arm64.tar.gz\" -C \"${OUT_BASE}/darwin/arm64\" tweeter\n          tar -czvf \"${OUT_BASE}/windows/amd64/tweeter_windows_amd64.tar.gz\" -C \"${OUT_BASE}/windows/amd64\" tweeter.exe\n          tar -czvf \"${OUT_BASE}/linux/amd64/tweeter_linux_amd64.tar.gz\" -C \"${OUT_BASE}/linux/amd64\" tweeter\n          tar -czvf \"${OUT_BASE}/linux/arm64/tweeter_linux_arm64.tar.gz\" -C \"${OUT_BASE}/linux/arm64\" tweeter\n      - name: create release\n        env:\n          OUT_BASE: ./bin/${{ env.RELEASE_VERSION }}\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: |\n          jq -r .body tmp-release-notes.json > tmp-release-notes.md\n          gh release create ${{ env.RELEASE_VERSION }} \\\n            -t \"$(jq -r .name tmp-release-notes.json)\" \\\n            -F tmp-release-notes.md \\\n            \"${OUT_BASE}/darwin/amd64/tweeter_darwin_amd64.tar.gz#tweeter_osx_amd64\" \\\n            \"${OUT_BASE}/darwin/arm64/tweeter_darwin_arm64.tar.gz#tweeter_osx_arm64\" \\\n            \"${OUT_BASE}/windows/amd64/tweeter_windows_amd64.tar.gz#tweeter_windows_amd64\" \\\n            \"${OUT_BASE}/linux/amd64/tweeter_linux_amd64.tar.gz#tweeter_linux_amd64\" \\\n            \"${OUT_BASE}/linux/arm64/tweeter_linux_arm64.tar.gz#tweeter_linux_arm64\"\n"
  },
  {
    "path": "chapter/10/.gitignore",
    "content": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n# Test binary, built with `go test -c`\n*.test\n\n# Output of the go coverage tool, specifically when used with LiteIDE\n*.out\n\n# Dependency directories (remove the comment below to include it)\n# vendor/\n\n.idea"
  },
  {
    "path": "chapter/10/Dockerfile",
    "content": "FROM golang:1.17 as builder\nWORKDIR /workspace\n\n# Run this with docker build --build_arg $(go env GOPROXY) to override the goproxy\nARG goproxy=https://proxy.golang.org\nENV GOPROXY=$goproxy\n\n# Copy the Go Modules manifests\nCOPY go.mod go.mod\nCOPY go.sum go.sum\n# Cache deps before building and copying source so that we don't need to re-download as much\n# and so that source changes don't invalidate our downloaded layer\nRUN go mod download\n\n# Copy the sources\nCOPY ./ ./\n\nRUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \\\n    go build -a -ldflags '-extldflags \"-static\"' \\\n    -o tweeter .\n\n# Copy the action into a thin image\nFROM gcr.io/distroless/static:latest\nWORKDIR /\nCOPY --from=builder /workspace/tweeter .\nENTRYPOINT [\"/tweeter\"]\n"
  },
  {
    "path": "chapter/10/README.md",
    "content": "# DevOps for Go Tweeter\nThe tweeter command line tool will send a tweet via Twitter.\n\n## Setup\nYou can use tweeter to send a tweet or to output the message to STDOUT. If you want to send a tweet, you will need to set up a Twitter application.\n\n### Setup With a Twitter Application\nTo send a tweet, you will need to create or use an existing Twitter account, create a Twitter application, and generate API credentials. All of this can be done through the\n[Twitter Developer Portal](https://developer.twitter.com/en/portal/projects-and-apps).\n\n### Setup Without a Twitter Application\nSome people may not want to set up a Twitter account. If you would like to use tweeter without sending tweets, use the `--dry-run` argument. This will cause the tool to write the message to STDOUT rather than sending the message to Twitter.\n\n## Inputs\n\n- `--message` **Required** the tweet message you would like to send\n- `--apiKey` the API key under Consumer Keys in the [Twitter developer portal](https://developer.twitter.com/en/portal/projects-and-apps)\n- `--apiKeySecret` the API key secret under Consumer Keys in the [Twitter developer portal](https://developer.twitter.com/en/portal/projects-and-apps)\n- `--accessToken` the access token under Authentication Tokens in the [Twitter developer portal](https://developer.twitter.com/en/portal/projects-and-apps)\n- `--accessTokenSecret` the access token secret under Authentications Tokens in the [Twitter developer portal](https://developer.twitter.com/en/portal/projects-and-apps)\n- `--dryRun` will skip authentication validation and sending the message to Twitter\n\n## Test\n```\n$ go test ./...\n?   \tgithub.com/devopsforgo/github-actions\t[no test files]\nok  \tgithub.com/devopsforgo/github-actions/pkg/tweeter\t0.002s\n```\n\n## Run Help\nTo see the command line arguments and descriptions, display the help.\n```\n$ go run . -h\nUsage of /tmp/go-build3731631588/b001/exe/github-actions:\n      --accessToken string         twitter access token\n      --accessTokenSecret string   twitter access token secret\n      --apiKey string              twitter api key\n      --apiKeySecret string        twitter api key secret\n      --dryRun                     if true, then a tweet will not be sent\n      --message string             message you'd like to send to twitter\npflag: help requested\nexit status 2\n```\n\n## Run Without Sending a Tweet\nThe `--dryRun` argument will skip validation of the authentication arguments and output the message to STDOUT.\n```\n$ go run . --dryRun --message foo\n```\n\n## Run Sending a Tweet\nWithout `--dryRun` specified, tweeter will send the `--messsage` argument as a Tweet.\n```\n$ go run . --message foo --apiKey 123 --apiKeySecret secret --accessToken token --accessTokenSecret secret\n```"
  },
  {
    "path": "chapter/10/action.yaml",
    "content": "name: Tweeter Action\nauthor: DevOps for Go\ndescription: Simple action to send a tweet via an GitHub Action.\ninputs:\n  message:\n    description: 'message you want to tweet'\n    required: true\n  apiKey:\n    description: 'api key for Twitter api'\n    required: true\n  apiKeySecret:\n    description: 'api key secret for Twitter api'\n    required: true\n  accessToken:\n    description: 'access token for Twitter api'\n    required: true\n  accessTokenSecret:\n    description: 'access token secret for Twitter api'\n    required: true\noutputs:\n  errorMessage:\n    description: 'if something went wrong, the error message'\n  sentMessage:\n    description: 'message sent to Twitter'\nruns:\n  using: docker\n  image: docker://ghcr.io/PacktPublishing/tweeter:1.0.0\n  args:\n    - --message\n    - \"${{ inputs.message }}\"\n    - --apiKey\n    - ${{ inputs.apiKey }}\n    - --apiKeySecret\n    - ${{ inputs.apiKeySecret }}\n    - --accessToken\n    - ${{ inputs.accessToken }}\n    - --accessTokenSecret\n    - ${{ inputs.accessTokenSecret }}\n"
  },
  {
    "path": "chapter/10/go.mod",
    "content": "module github.com/PacktPublishing/Go-for-DevOps/chapter/10\n\ngo 1.17\n\nrequire (\n\tgithub.com/dghubble/go-twitter v0.0.0-20210609183100-2fdbf421508e\n\tgithub.com/dghubble/oauth1 v0.7.0\n\tgithub.com/hashicorp/go-multierror v1.1.1\n\tgithub.com/pkg/errors v0.9.1\n\tgithub.com/spf13/pflag v1.0.5\n)\n\nrequire (\n\tgithub.com/cenkalti/backoff v2.1.1+incompatible // indirect\n\tgithub.com/dghubble/sling v1.3.0 // indirect\n\tgithub.com/google/go-querystring v1.0.0 // indirect\n\tgithub.com/hashicorp/errwrap v1.0.0 // indirect\n)\n"
  },
  {
    "path": "chapter/10/go.sum",
    "content": "github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY=\ngithub.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=\ngithub.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dghubble/go-twitter v0.0.0-20210609183100-2fdbf421508e h1:o0sI/cfhAXdtAbiIVeTd4hmwtwgs4cQFwRBhmbx8AKY=\ngithub.com/dghubble/go-twitter v0.0.0-20210609183100-2fdbf421508e/go.mod h1:xfg4uS5LEzOj8PgZV7SQYRHbG7jPUnelEiaAVJxmhJE=\ngithub.com/dghubble/oauth1 v0.7.0 h1:AlpZdbRiJM4XGHIlQ8BuJ/wlpGwFEJNnB4Mc+78tA/w=\ngithub.com/dghubble/oauth1 v0.7.0/go.mod h1:8pFdfPkv/jr8mkChVbNVuJ0suiHe278BtWI4Tk1ujxk=\ngithub.com/dghubble/sling v1.3.0 h1:pZHjCJq4zJvc6qVQ5wN1jo5oNZlNE0+8T/h0XeXBUKU=\ngithub.com/dghubble/sling v1.3.0/go.mod h1:XXShWaBWKzNLhu2OxikSNFrlsvowtz4kyRuXUG7oQKY=\ngithub.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=\ngithub.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=\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-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=\ngithub.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\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/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\n"
  },
  {
    "path": "chapter/10/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/hashicorp/go-multierror\"\n\t\"github.com/pkg/errors\"\n\tflag \"github.com/spf13/pflag\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/10/pkg/tweeter\"\n)\n\nvar (\n\tmessage, apiKey, apiKeySecret, accessToken, accessTokenSecret string\n\tdryRun, versionFlag                                           bool\n)\n\nfunc main() {\n\tparseAndValidateInput()\n\n\tif versionFlag {\n\t\tprintVersion()\n\t\treturn\n\t}\n\n\tif dryRun {\n\t\tprintOutput(\"sentMessage\", message)\n\t\treturn\n\t}\n\n\ttweeterClient, err := tweeter.New(tweeter.Config{\n\t\tApiKey:            apiKey,\n\t\tApiKeySecret:      apiKeySecret,\n\t\tAccessToken:       accessToken,\n\t\tAccessTokenSecret: accessTokenSecret,\n\t})\n\n\tif err != nil {\n\t\terr = errors.Wrap(err, \"failed creating tweeter client\")\n\t\tprintOutput(\"errorMessage\", err.Error())\n\t\tos.Exit(1)\n\t}\n\n\t// send the tweet\n\tif err := tweeterClient.Tweet(message); err != nil {\n\t\terr = errors.Wrap(err, \"status update error\")\n\t\tprintOutput(\"errorMessage\", err.Error())\n\t\tos.Exit(1)\n\t}\n\n\tprintOutput(\"sentMessage\", message)\n}\n\nfunc parseAndValidateInput() {\n\tflag.StringVar(&message, \"message\", \"\", \"message you'd like to send to twitter\")\n\tflag.StringVar(&apiKey, \"apiKey\", \"\", \"twitter api key\")\n\tflag.StringVar(&apiKeySecret, \"apiKeySecret\", \"\", \"twitter api key secret\")\n\tflag.StringVar(&accessToken, \"accessToken\", \"\", \"twitter access token\")\n\tflag.StringVar(&accessTokenSecret, \"accessTokenSecret\", \"\", \"twitter access token secret\")\n\tflag.BoolVar(&dryRun, \"dryRun\", false, \"if true or if env var DRY_RUN=true, then a tweet will not be sent\")\n\tflag.BoolVar(&versionFlag, \"version\", false, \"output the version of tweeter\")\n\tflag.Parse()\n\n\tif os.Getenv(\"DRY_RUN\") == \"true\" {\n\t\tdryRun = true\n\t}\n\n\tif versionFlag {\n\t\treturn\n\t}\n\n\tvar err error\n\tif message == \"\" {\n\t\terr = multierror.Append(err, errors.New(\"--message can't be empty\"))\n\t}\n\n\tif !dryRun {\n\t\tif apiKey == \"\" {\n\t\t\terr = multierror.Append(err, errors.New(\"--apiKey can't be empty\"))\n\t\t}\n\n\t\tif apiKeySecret == \"\" {\n\t\t\terr = multierror.Append(err, errors.New(\"--apiKeySecret can't be empty\"))\n\t\t}\n\n\t\tif accessToken == \"\" {\n\t\t\terr = multierror.Append(err, errors.New(\"--accessToken can't be empty\"))\n\t\t}\n\n\t\tif accessTokenSecret == \"\" {\n\t\t\terr = multierror.Append(err, errors.New(\"--accessTokenSecret can't be empty\"))\n\t\t}\n\t}\n\n\tif err != nil {\n\t\t_, _ = fmt.Fprint(os.Stderr, err.Error())\n\t\tos.Exit(1)\n\t}\n}\n\nfunc printVersion() {\n\tversionStr := \"dirty\"\n\tif tweeter.Version != \"\" {\n\t\tversionStr = tweeter.Version\n\t}\n\tfmt.Printf(\"tweeter version: %s\", versionStr)\n}\n\nfunc printOutput(key, message string) {\n\tfmt.Printf(\"::set-output name=%s::%s\\n\", key, message)\n}\n"
  },
  {
    "path": "chapter/10/pkg/tweeter/tweeter.go",
    "content": "package tweeter\n\nimport (\n\t\"github.com/dghubble/go-twitter/twitter\"\n\t\"github.com/dghubble/oauth1\"\n\t\"github.com/hashicorp/go-multierror\"\n\t\"github.com/pkg/errors\"\n)\n\nvar (\n\t// Version is the git reference injected at build\n\tVersion string\n)\n\ntype (\n\t// Config is the authentication params needed to construct a tweeter.Client\n\tConfig struct {\n\t\tApiKey            string\n\t\tApiKeySecret      string\n\t\tAccessToken       string\n\t\tAccessTokenSecret string\n\t}\n\n\t// Client sends tweets to Twitter\n\tClient struct {\n\t\ttwitterClient *twitter.Client\n\t}\n)\n\n// Validate will check the Config to ensure its field values are valid\nfunc (cfg Config) Validate() error {\n\tvar err error\n\tif cfg.ApiKey == \"\" {\n\t\terr = multierror.Append(err, errors.New(\"ApiKey is required\"))\n\t}\n\n\tif cfg.ApiKeySecret == \"\" {\n\t\terr = multierror.Append(err, errors.New(\"ApiKeySecret is required\"))\n\t}\n\n\tif cfg.AccessToken == \"\" {\n\t\terr = multierror.Append(err, errors.New(\"AccessToken is required\"))\n\t}\n\n\tif cfg.AccessTokenSecret == \"\" {\n\t\terr = multierror.Append(err, errors.New(\"AccessTokenSecret is required\"))\n\t}\n\treturn err\n}\n\n// New creates a new instance of the tweeter.Client ready to send tweets\nfunc New(cfg Config) (*Client, error) {\n\tif err := cfg.Validate(); err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed to validate tweeter config\")\n\t}\n\n\tvar (\n\t\toauthCfg   = oauth1.NewConfig(cfg.ApiKey, cfg.ApiKeySecret)\n\t\ttoken      = oauth1.NewToken(cfg.AccessToken, cfg.AccessTokenSecret)\n\t\thttpClient = oauthCfg.Client(oauth1.NoContext, token)\n\t)\n\n\treturn &Client{\n\t\ttwitterClient: twitter.NewClient(httpClient),\n\t}, nil\n}\n\n// Tweet sends a tweet\nfunc (c *Client) Tweet(message string) error {\n\t_, _, err := c.twitterClient.Statuses.Update(message, nil)\n\treturn errors.Wrap(err, \"failed to send tweet\")\n}\n"
  },
  {
    "path": "chapter/10/pkg/tweeter/tweeter_test.go",
    "content": "package tweeter_test\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/10/pkg/tweeter\"\n)\n\nfunc TestNew(t *testing.T) {\n\tsubject, err := tweeter.New(tweeter.Config{\n\t\tApiKey:            \"apiKey\",\n\t\tApiKeySecret:      \"apiKeySecret\",\n\t\tAccessToken:       \"accessToken\",\n\t\tAccessTokenSecret: \"accessTokenSecret\",\n\t})\n\n\tif err != nil {\n\t\tt.Error(err, \"config should be valid\")\n\t}\n\n\tif subject == nil {\n\t\tt.Error(\"subject should not be nil\")\n\t}\n}\n\nfunc TestConfig_Validate(t *testing.T) {\n\ttestCases := []struct {\n\t\tName   string\n\t\tCfg    tweeter.Config\n\t\tExpect func(t *testing.T, err error)\n\t}{\n\t\t{\n\t\t\tName: \"All keys are filled\",\n\t\t\tCfg: tweeter.Config{\n\t\t\t\tApiKey:            \"apiKey\",\n\t\t\t\tApiKeySecret:      \"apiKeySecret\",\n\t\t\t\tAccessToken:       \"accessToken\",\n\t\t\t\tAccessTokenSecret: \"accessTokenSecret\",\n\t\t\t},\n\t\t\tExpect: func(t *testing.T, err error) {\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err, \"error should be nil\")\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName: \"ApiKey is empty\",\n\t\t\tCfg: tweeter.Config{\n\t\t\t\tApiKey:            \"\",\n\t\t\t\tApiKeySecret:      \"apiKeySecret\",\n\t\t\t\tAccessToken:       \"accessToken\",\n\t\t\t\tAccessTokenSecret: \"accessTokenSecret\",\n\t\t\t},\n\t\t\tExpect: func(t *testing.T, err error) {\n\t\t\t\tif err == nil {\n\t\t\t\t\tt.Error(\"error should be non-nil\")\n\t\t\t\t}\n\n\t\t\t\tif !strings.Contains(err.Error(), \"ApiKey is required\") {\n\t\t\t\t\tt.Error(err.Error(), \"should contain 'ApiKey is required'\")\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName: \"ApiKeySecret is empty\",\n\t\t\tCfg: tweeter.Config{\n\t\t\t\tApiKey:            \"apiKey\",\n\t\t\t\tApiKeySecret:      \"\",\n\t\t\t\tAccessToken:       \"accessToken\",\n\t\t\t\tAccessTokenSecret: \"accessTokenSecret\",\n\t\t\t},\n\t\t\tExpect: func(t *testing.T, err error) {\n\t\t\t\tif err == nil {\n\t\t\t\t\tt.Error(\"error should be non-nil\")\n\t\t\t\t}\n\n\t\t\t\tif !strings.Contains(err.Error(), \"ApiKeySecret is required\") {\n\t\t\t\t\tt.Error(err.Error(), \"should contain 'ApiKeySecret is required'\")\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName: \"AccessToken is empty\",\n\t\t\tCfg: tweeter.Config{\n\t\t\t\tApiKey:            \"apiKey\",\n\t\t\t\tApiKeySecret:      \"apiKeySecret\",\n\t\t\t\tAccessToken:       \"\",\n\t\t\t\tAccessTokenSecret: \"accessTokenSecret\",\n\t\t\t},\n\t\t\tExpect: func(t *testing.T, err error) {\n\t\t\t\tif err == nil {\n\t\t\t\t\tt.Error(\"error should be non-nil\")\n\t\t\t\t}\n\n\t\t\t\tif !strings.Contains(err.Error(), \"AccessToken is required\") {\n\t\t\t\t\tt.Error(err.Error(), \"should contain 'AccessToken is required'\")\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName: \"AccessTokenSecret is empty\",\n\t\t\tCfg: tweeter.Config{\n\t\t\t\tApiKey:            \"apiKey\",\n\t\t\t\tApiKeySecret:      \"apiKeySecret\",\n\t\t\t\tAccessToken:       \"accessToken\",\n\t\t\t\tAccessTokenSecret: \"\",\n\t\t\t},\n\t\t\tExpect: func(t *testing.T, err error) {\n\t\t\t\tif err == nil {\n\t\t\t\t\tt.Error(\"error should be non-nil\")\n\t\t\t\t}\n\n\t\t\t\tif !strings.Contains(err.Error(), \"AccessTokenSecret is required\") {\n\t\t\t\t\tt.Error(err.Error(), \"should contain 'AccessTokenSecret is required'\")\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName: \"ApiKey and AccessTokenSecret are empty\",\n\t\t\tCfg: tweeter.Config{\n\t\t\t\tApiKey:            \"\",\n\t\t\t\tApiKeySecret:      \"apiKeySecret\",\n\t\t\t\tAccessToken:       \"accessToken\",\n\t\t\t\tAccessTokenSecret: \"\",\n\t\t\t},\n\t\t\tExpect: func(t *testing.T, err error) {\n\t\t\t\tif err == nil {\n\t\t\t\t\tt.Error(\"error should be non-nil\")\n\t\t\t\t}\n\n\t\t\t\tif !strings.Contains(err.Error(), \"AccessTokenSecret is required\") ||\n\t\t\t\t\t!strings.Contains(err.Error(), \"ApiKey is required\") {\n\t\t\t\t\tt.Error(err.Error(), \"should contain 'AccessTokenSecret is required' and 'ApiKey is required'\")\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc\n\t\tt.Run(tc.Name, func(t *testing.T) {\n\t\t\ttc.Expect(t, tc.Cfg.Validate())\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "chapter/11/Dockerfile",
    "content": "FROM golang:1.17\nCOPY . /usr/src/server\nWORKDIR /usr/src/server\nRUN go env -w GOPROXY=direct GO111MODULE=on\nRUN go mod init github.com/PacktPublishing/Go-for-DevOps/chapter/11\nRUN go mod tidy\nWORKDIR /usr/src/server/ops\nRUN go install\nCMD [\"/go/bin/ops\", \"--jaegerAddr=jaeger-all-in-one:16685\", \"--promAddr=prometheus:9000\", \"--petstoreAddr=petstore:6742\"]\n"
  },
  {
    "path": "chapter/11/README.md",
    "content": "# ChatOps Demonstration\n\nThis directory contains:\n\n* A ChatOps service for Slack\n* An traceable and metricized Petstore CRUD service\n* Jaeger for Trace storage\n* Prometheus for Metric storage\n\n## Turnup the demo\n\nIn this directory, use the following command:\n\n```bash\ndocker-compose up -d \n```\n\nIn a another terminal enter the following directory:\n```\nchapter/11/chatops/\n```\n\nCreate a `.env` file with the following content:\n\n```bash\nAUTH_TOKEN=xoxb-[the rest of the token] \nAPP_TOKEN=xapp-[the rest of the token] \n```\n\nThese were generated when we setup the Slack app. Please see chapter 11 for more details on generating these.\n\nRun the bot with:\n\n```bash\ngo run chatbotops.go \n```\n\nIn the background, there is a demo client that is adding pets to the petstore and doing searches for pets (some searches will cause errors). The service is set to Float sampling, so not every call will generate a trace. \n\nIn another terminal, you can interact with the petstore by using the CLI application. This will let you add your own pets, delete pets, search for pets with a filter. That client can be found here: chapter/11/petstore/client/cli/petstore. You can find instructions on its use by running the following: \n\n```bash\ngo run petstore.go --help\n```\n\n\n## Turndown the demo\n\nThis consists of:\n\n* Cancelling the running chatbot.go binary\n* Running `docker-compose down` in this directory\n"
  },
  {
    "path": "chapter/11/chatbot/.gitignore",
    "content": ".env"
  },
  {
    "path": "chapter/11/chatbot/bot/bot.go",
    "content": "// Package bot defines a basic slack bot that can listen for app mention events for our bot\n// and send the message to a handler to handle the interaction.\npackage bot\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/slack-go/slack\"\n\t\"github.com/slack-go/slack/slackevents\"\n\t\"github.com/slack-go/slack/socketmode\"\n)\n\n// HandleFunc receive the user who sent a message and the message. It can then use api or client to respond to said message.\ntype HandleFunc func(ctx context.Context, m Message)\n\n// Message details information about a message that was sent in an AppMention event.\ntype Message struct {\n\t// User has the user information on who mentioned the bot.\n\tUser *slack.User\n\t// AppMention gives information on the event.\n\tAppMention *slackevents.AppMentionEvent\n\t// Text gives the text of the message without the @User stuff. If you want the full message, see AppMention.\n\tText string\n}\n\ntype register struct {\n\tr *regexp.Regexp\n\th HandleFunc\n}\n\n// Bot provides a slack bot for listening to slack channels.\ntype Bot struct {\n\tapi    *slack.Client\n\tclient *socketmode.Client\n\tctx    context.Context\n\tcancel context.CancelFunc\n\n\tdefaultHandler HandleFunc\n\treg            []register\n}\n\n// New creates a new Bot.\nfunc New(api *slack.Client, client *socketmode.Client) (*Bot, error) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tb := &Bot{\n\t\tapi:    api,\n\t\tclient: client,\n\t\tctx:    ctx,\n\t\tcancel: cancel,\n\t}\n\treturn b, nil\n}\n\n// Start starts listening for events from the socket client. This blocks until the client dies\n// or Stop() is called.\nfunc (b *Bot) Start() {\n\tgo b.loop()\n\n\tb.client.RunContext(b.ctx)\n}\n\n// Stop stops the bot. The bot cannot be reused after this.\nfunc (b *Bot) Stop() {\n\tb.cancel()\n}\n\n// Register registers a function for handling a message to the bot. The regex is checked in the order\n// that it is added. A nil regexp is considered the default handler. Only 1 default handler can be added and\n// is always the choice of last resort.\nfunc (b *Bot) Register(r *regexp.Regexp, h HandleFunc) {\n\tif h == nil {\n\t\tpanic(\"HandleFunc cannot be nil\")\n\t}\n\tif r == nil {\n\t\tif b.defaultHandler != nil {\n\t\t\tpanic(\"cannot add two default handles\")\n\t\t}\n\t\tb.defaultHandler = h\n\t\treturn\n\t}\n\tb.reg = append(b.reg, register{r, h})\n}\n\n// loop is the event loop.\nfunc (b *Bot) loop() {\n\tfor {\n\t\tctx := context.Background()\n\t\tselect {\n\t\tcase <-b.ctx.Done():\n\t\t\treturn\n\t\tcase evt := <-b.client.Events:\n\t\t\tswitch evt.Type {\n\t\t\tcase socketmode.EventTypeConnecting, socketmode.EventTypeConnected:\n\t\t\tcase socketmode.EventTypeConnectionError:\n\t\t\t\tlog.Println(\"connection failed. Retrying later...\")\n\t\t\tcase socketmode.EventTypeEventsAPI:\n\t\t\t\tdata, ok := evt.Data.(slackevents.EventsAPIEvent)\n\t\t\t\tif !ok {\n\t\t\t\t\tlog.Printf(\"bug: got %T which should be a slackevents.EventsAPIEvent\", evt.Data)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tb.client.Ack(*evt.Request)\n\t\t\t\tgo b.appMentioned(ctx, data)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// appMentioned handles an event socketmode.EventTypeEventsAPI that had a .Data that is a slackevents.EventsAPIEvent that eventually\n// is a AppMentionEvent. This has a crazy amount of freaking event wrapping.\nfunc (b *Bot) appMentioned(ctx context.Context, data slackevents.EventsAPIEvent) {\n\tswitch data.Type {\n\tcase slackevents.CallbackEvent:\n\t\tcallback := data.Data.(*slackevents.EventsAPICallbackEvent)\n\n\t\tswitch ev := data.InnerEvent.Data.(type) {\n\t\tcase *slackevents.AppMentionEvent:\n\t\t\tif ev.BotID != \"\" {\n\t\t\t\t_, _, err := b.api.PostMessage(ev.Channel, slack.MsgOptionText(\"I don't talk to other bots\", false))\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Printf(\"failed posting message: %v\", err)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tmsg, err := b.makeMsg(callback, ev)\n\t\t\tif err != nil {\n\t\t\t\tlog.Println(err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor _, reg := range b.reg {\n\t\t\t\tif reg.r.MatchString(msg.Text) {\n\t\t\t\t\treg.h(ctx, msg)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tif b.defaultHandler != nil {\n\t\t\t\tb.defaultHandler(ctx, msg)\n\t\t\t}\n\t\t}\n\tdefault:\n\t\tb.client.Debugf(\"unsupported Events API event received\")\n\t}\n}\n\n// makeMsg extracts the user and text from an event and callback into a Message type.\nfunc (b *Bot) makeMsg(callback *slackevents.EventsAPICallbackEvent, event *slackevents.AppMentionEvent) (Message, error) {\n\tuser, err := b.api.GetUserInfo(event.User)\n\tif err != nil {\n\t\treturn Message{}, fmt.Errorf(\"could not get user data: %w\", err)\n\t}\n\trm := rawMessage{}\n\tif err := json.Unmarshal(*callback.InnerEvent, &rm); err != nil {\n\t\treturn Message{}, fmt.Errorf(\"bot received a callback with no InnerEvent: %w\", err)\n\t}\n\treturn Message{User: user, AppMention: event, Text: rm.getText()}, nil\n}\n\n// rawMessage is used to covert a slackevents.EventsAPICallbackEvent.InnerEvent, which is the raw JSON, into\n// a form in which I can abstract the message sent by the user without things like @user in it. This is\n// not carried into the exposed Go type and I want to use Slack's pre-filtering instead of doing it myself.\ntype rawMessage struct {\n\tBlocks []interface{}\n}\n\n// getText gets the text without all the extra @user stuff.\nfunc (r rawMessage) getText() string {\n\tfor _, block := range r.Blocks {\n\t\tblockReal := block.(map[string]interface{})\n\t\tif blockReal[\"type\"] != \"rich_text\" {\n\t\t\tcontinue\n\t\t}\n\t\telements := blockReal[\"elements\"].([]interface{})\n\t\tfor _, el := range elements {\n\t\t\telReal := el.(map[string]interface{})\n\t\t\tif elReal[\"type\"].(string) != \"rich_text_section\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tsubElements := elReal[\"elements\"].([]interface{})\n\t\t\tfor _, subEl := range subElements {\n\t\t\t\tsubElReal := subEl.(map[string]interface{})\n\t\t\t\tif subElReal[\"type\"] != \"text\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\treturn strings.TrimSpace(subElReal[\"text\"].(string))\n\t\t\t}\n\t\t}\n\t}\n\treturn \"\"\n}\n"
  },
  {
    "path": "chapter/11/chatbot/chatbot.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/chatbot/bot\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/chatbot/internal/handlers\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/ops/client\"\n\n\t\"github.com/joho/godotenv\"\n\t\"github.com/slack-go/slack\"\n\t\"github.com/slack-go/slack/socketmode\"\n)\n\nvar (\n\topsAddr = flag.String(\"opsAddr\", \"127.0.0.1:7000\", \"The address the Ops service runs on.\")\n\tdebug   = flag.Bool(\"debug\", false, \"If turned on will log debug information to the screen.\")\n)\n\nfunc main() {\n\tflag.Parse()\n\n\terr := godotenv.Load()\n\tif err != nil {\n\t\tpanic(\"could not load .env file\")\n\t}\n\n\tapi := slack.New(\n\t\tos.Getenv(\"AUTH_TOKEN\"),\n\t\tslack.OptionAppLevelToken(os.Getenv(\"APP_TOKEN\")),\n\t\tslack.OptionDebug(*debug),\n\t)\n\n\tsmClient := socketmode.New(\n\t\tapi,\n\t\tsocketmode.OptionDebug(*debug),\n\t\tsocketmode.OptionLog(\n\t\t\tlog.New(\n\t\t\t\tos.Stdout,\n\t\t\t\t\"socketmode: \",\n\t\t\t\tlog.Lshortfile|log.LstdFlags,\n\t\t\t),\n\t\t),\n\t)\n\n\topsClient, err := client.New(*opsAddr)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tb, err := bot.New(api, smClient)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\th := handlers.Ops{OpsClient: opsClient, API: api, SMClient: smClient}\n\th.Register(b)\n\tlog.Println(\"Bot started\")\n\tb.Start()\n\n\tpanic(\"Bot stopped functioning\")\n}\n"
  },
  {
    "path": "chapter/11/chatbot/internal/handlers/handlers.go",
    "content": "// Package handlers provides an Ops type that has methods that implement bot.HandleFunc for various commands that could be sent to a bot.\npackage handlers\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/chatbot/bot\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/ops/client\"\n\n\t\"github.com/olekukonko/tablewriter\"\n\t\"github.com/slack-go/slack\"\n\t\"github.com/slack-go/slack/socketmode\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/11/ops/proto\"\n)\n\n// Ops provides bot.HandleFunc methods that can reuse the connections to the Ops service.\ntype Ops struct {\n\tOpsClient *client.Ops\n\tAPI       *slack.Client\n\tSMClient  *socketmode.Client\n}\n\n// write writes a formatted string to the event output in the bot.Message.\nfunc (o Ops) write(m bot.Message, s string, i ...interface{}) error {\n\t_, _, err := o.API.PostMessage(\n\t\tm.AppMention.Channel,\n\t\tslack.MsgOptionText(fmt.Sprintf(s, i...), false),\n\t)\n\treturn err\n}\n\n// Register registers all the commands held in Ops with the bot.\nfunc (o Ops) Register(b *bot.Bot) {\n\tb.Register(regexp.MustCompile(`^\\s*help`), o.Help)\n\tb.Register(regexp.MustCompile(`^\\s*list traces`), o.ListTraces)\n\tb.Register(regexp.MustCompile(`^\\s*show trace`), o.ShowTrace)\n\tb.Register(regexp.MustCompile(`^\\s*change sampling`), o.ChangeSampling)\n\tb.Register(regexp.MustCompile(`^\\s*show logs`), o.ShowLogs)\n\tb.Register(nil, o.lastResort)\n}\n\n// opt stores the key/value pair for an option to a command.\ntype opt struct {\n\tkey string\n\tval string\n}\n\n// listTracesRE teases the options from a `list traces` command.\nvar listTracesRE = regexp.MustCompile(`(\\S+)=(?:(\\S+))`)\n\n// ListTraces lists all the traces requested in a table that is output to the user.\nfunc (o Ops) ListTraces(ctx context.Context, m bot.Message) {\n\tsp := strings.Split(m.Text, \"list traces\")\n\tif len(sp) != 2 {\n\t\to.write(m, \"The 'list traces' command is malformed\")\n\t\treturn\n\t}\n\tt := strings.TrimSpace(sp[1])\n\n\tkvOpts := []opt{}\n\tfor _, match := range listTracesRE.FindAllStringSubmatch(t, -1) {\n\t\tkvOpts = append(kvOpts, opt{strings.TrimSpace(match[1]), strings.TrimSpace(match[2])})\n\t}\n\toptions := []client.CallOption{}\n\n\tfor _, opt := range kvOpts {\n\t\tswitch opt.key {\n\t\tcase \"operation\":\n\t\t\toptions = append(options, client.WithOperation(opt.val))\n\t\tcase \"start\":\n\t\t\tt, err := time.Parse(`01/02/2006-15:04:05`, opt.val)\n\t\t\tif err != nil {\n\t\t\t\to.write(m, \"The start option must be in the form `01/02/2006-15:04:05` for UTC\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\toptions = append(options, client.WithStart(t))\n\t\tcase \"end\":\n\t\t\tif opt.val == \"now\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tt, err := time.Parse(`01/02/2006-15:04:05`, opt.val)\n\t\t\tif err != nil {\n\t\t\t\to.write(m, \"The end option must be in the form `01/02/2006-15:04:05` for UTC\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\toptions = append(options, client.WithEnd(t))\n\t\tcase \"limit\":\n\t\t\ti, err := strconv.Atoi(opt.val)\n\t\t\tif err != nil {\n\t\t\t\to.write(m, \"The limit option must be an integer\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif i > 100 {\n\t\t\t\to.write(m, \"Cannot request more than 100 traces\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\toptions = append(options, client.WithLimit(int32(i)))\n\t\tcase \"tags\":\n\t\t\ttags, err := convertList(opt.val)\n\t\t\tif err != nil {\n\t\t\t\to.write(m, \"tags: must enclosed in [], like tags=[tag,tag2]\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\toptions = append(options, client.WithLabels(tags))\n\t\tdefault:\n\t\t\to.write(m, \"don't understand an option type(%s)\", opt.key)\n\t\t\treturn\n\t\t}\n\t}\n\ttraces, err := o.OpsClient.ListTraces(ctx, options...)\n\tif err != nil {\n\t\to.write(m, \"Ops server had an error: %s\", err)\n\t\treturn\n\t}\n\tb := strings.Builder{}\n\tb.WriteString(\"Here are the traces you requested:\\n\")\n\n\ttable := tablewriter.NewWriter(&b)\n\ttable.SetHeader([]string{\"Start Time(UTC)\", \"Trace ID\"})\n\tfor _, item := range traces {\n\t\ttable.Append(\n\t\t\t[]string{\n\t\t\t\titem.Start.Format(\"01/02/2006 04:05\"),\n\t\t\t\t\"http://127.0.0.1:16686/trace/\" + item.ID,\n\t\t\t},\n\t\t)\n\t}\n\ttable.Render()\n\to.write(m, b.String())\n}\n\n// ShowTrace gives the URL to a trace ID.\nfunc (o Ops) ShowTrace(ctx context.Context, m bot.Message) {\n\tsp := strings.Split(m.Text, \"show trace\")\n\tif len(sp) != 2 {\n\t\to.write(m, `show trace command should be in form: show trace <id>`)\n\t\treturn\n\t}\n\tid := strings.TrimSpace(sp[1])\n\n\ttrace, err := o.OpsClient.ShowTrace(ctx, id)\n\tif err != nil {\n\t\to.write(m, \"Ops server had an error: %s\", err)\n\t\treturn\n\t}\n\n\tb := strings.Builder{}\n\ttable := tablewriter.NewWriter(&b)\n\tb.WriteString(\"Here is some basic trace data:\\n\")\n\ttable.Append([]string{\"ID\", trace.Id})\n\ttable.Append([]string{\"Duration\", trace.Duration.AsDuration().String()})\n\ttable.Append([]string{\"Jaeger URL\", \"http://127.0.0.1:16686/trace/\" + trace.Id})\n\tif len(trace.Errors) > 0 {\n\t\ttable.Append([]string{\"Had Errors\", \"true\"})\n\t} else {\n\t\ttable.Append([]string{\"Had Errors\", \"false\"})\n\t}\n\ttable.Render()\n\tb.WriteString(\"\\n\")\n\n\tif len(trace.Errors) > 0 {\n\t\ttable = tablewriter.NewWriter(&b)\n\t\tb.WriteString(\"Here are the errors from the trace:\\n\")\n\t\tfor _, err := range trace.Errors {\n\t\t\ttable.Append([]string{err})\n\t\t}\n\t\ttable.Render()\n\t\tb.WriteString(\"\\n\")\n\t}\n\n\tb.WriteString(\"Here are the operations in the trace:\\n\")\n\ttable = tablewriter.NewWriter(&b)\n\tfor _, op := range trace.Operations {\n\t\ttable.Append([]string{op})\n\t}\n\ttable.Render()\n\tb.WriteString(\"\\n\")\n\n\to.write(m, \"%s,\\nHere is the trace info you requested:\\n\\n%s\", m.User.Name, b.String())\n}\n\n// ShowLogs outputs the logs given a trace ID.\nfunc (o Ops) ShowLogs(ctx context.Context, m bot.Message) {\n\tsp := strings.Split(m.Text, \"show logs\")\n\tif len(sp) != 2 {\n\t\to.write(m, `show logs command should be in form: show logs <id>`)\n\t\treturn\n\t}\n\tid := strings.TrimSpace(sp[1])\n\tlog.Println(\"show logs id==\", id)\n\tlogs, err := o.OpsClient.ShowLogs(ctx, id)\n\tif err != nil {\n\t\to.write(m, \"Ops server had an error: %s\", err)\n\t\treturn\n\t}\n\n\tb := strings.Builder{}\n\tn := time.Now().UTC()\n\tfor _, l := range logs {\n\t\tvar t string\n\t\tif l.Time.Year() == n.Year() && l.Time.Month() == n.Month() && l.Time.Day() == n.Day() {\n\t\t\tt = l.Time.Format(`15:04:05`)\n\n\t\t} else {\n\t\t\tt = l.Time.Format(`a01/02/2006 15:04:05`)\n\t\t}\n\t\tb.WriteString(fmt.Sprintf(\"%s: %s: %s\\n\", t, l.Key, l.Value))\n\t}\n\n\to.write(m, \"%s,\\nHere are the logs you requested for trace %s:\\n\\n%s\", m.User.Name, id, b.String())\n}\n\nvar sampleTypeRE = regexp.MustCompile(`^\\s*(never|always|float)`)\n\n// ChangeSampling changes the sampling type/rate on the server.\nfunc (o Ops) ChangeSampling(ctx context.Context, m bot.Message) {\n\tsp := strings.Split(m.Text, \"change sampling\")\n\tif len(sp) != 2 {\n\t\to.write(m, `change sampling command should be in form: change sampling <type> <options>`)\n\t\treturn\n\t}\n\tt := strings.TrimSpace((sp[1]))\n\n\tsub := sampleTypeRE.FindStringSubmatch(t)\n\tif len(sub) == 0 {\n\t\to.write(m, `I don't have support for the samplling type you requested, sorry...`)\n\t\treturn\n\t}\n\n\treq := &pb.ChangeSamplingReq{}\n\n\tswitch sub[1] {\n\tcase \"never\":\n\t\treq.Type = pb.SamplerType_STNever\n\tcase \"always\":\n\t\treq.Type = pb.SamplerType_STAlways\n\tcase \"float\":\n\t\treq.Type = pb.SamplerType_STFloat\n\n\t\tsp := strings.Split(t, \"float\")\n\t\tif len(sp) != 2 {\n\t\t\to.write(m, `'change sampling float' must be followed by a float that is > 0 and <= 1`)\n\t\t\treturn\n\t\t}\n\t\tf, err := strconv.ParseFloat(strings.TrimSpace(sp[1]), 64)\n\t\tif err != nil {\n\t\t\to.write(m, `'change sampling float' had an invalid float option: %q`, strings.TrimSpace(sp[1]))\n\t\t\treturn\n\t\t}\n\t\tif f <= 0 || f > 1 {\n\t\t\to.write(m, `'change sampling float' must be followed by a float that is > 0 and <= 1`)\n\t\t\treturn\n\t\t}\n\t\treq.FloatValue = f\n\tdefault:\n\t\to.write(m, `sorry, I hit a bug, I kinda understand %q, so you need to talk to my creator`, m.Text)\n\t\treturn\n\t}\n\n\terr := o.OpsClient.ChangeSampling(ctx, req)\n\tif err != nil {\n\t\to.write(m, \"Ops server gave an error on changing the sampling: %s\", err)\n\t\treturn\n\t}\n}\n\nvar cmdList string\n\nfunc init() {\n\tcmds := []string{}\n\tfor k := range help {\n\t\tcmds = append(cmds, k)\n\t}\n\tsort.Strings(cmds)\n\n\tb := strings.Builder{}\n\tfor _, cmd := range cmds {\n\t\tb.WriteString(cmd + \"\\n\")\n\t}\n\tb.WriteString(\"You can get more help by saying `help <cmd>` with a command from above.\\n\")\n\tcmdList = b.String()\n}\n\n// Help returns help about various commands.\nfunc (o Ops) Help(ctx context.Context, m bot.Message) {\n\tsp := strings.Split(m.Text, \"help\")\n\tif len(sp) < 2 {\n\t\to.write(m, \"%s,\\nYou have to give me a command you want help with\", m.User.Name)\n\t\treturn\n\t}\n\tcmd := strings.TrimSpace(strings.Join(sp[1:], \"\"))\n\tif cmd == \"\" {\n\t\to.write(m, \"Here are all the commands that I can help you with:\\n%s\", cmdList)\n\t\treturn\n\t}\n\n\tif v, ok := help[cmd]; ok {\n\t\to.write(m, \"I can help you waith that:\\n%s\", v)\n\t\treturn\n\t}\n\n\to.write(m, \"%s,\\nI don't know what %q is to give you help\", m.User.Name, cmd)\n}\n\nfunc (o Ops) lastResort(ctx context.Context, m bot.Message) {\n\to.write(m, \"%s,\\nI don't have anything that handles what you sent\", m.User.Name)\n}\n\nfunc convertList(s string) ([]string, error) {\n\tif string(s[0]) != `[` || string(s[len(s)-1]) != `]` {\n\t\treturn nil, errors.New(\"must enclosed in [], like [tag,tag2] comma deliminated with no spaces\")\n\t}\n\n\ts = strings.TrimPrefix(s, `[`)\n\ts = strings.TrimSuffix(s, `]`)\n\tsp := strings.Split(s, \",\")\n\ttags := []string{}\n\tfor _, t := range sp {\n\t\ttags = append(tags, strings.TrimSpace(t))\n\t}\n\treturn tags, nil\n}\n"
  },
  {
    "path": "chapter/11/chatbot/internal/handlers/help.go",
    "content": "package handlers\n\nvar help = map[string]string{\n\t\"list traces\": `\nlist traces <opt1=val1 op2=val2>\nEx: list traces operation=AddPets() limit=5\n\nlist traces returns a list of Open Telemetry traces. Various\noptions are provided to allow for filtering what traces you see.\n\nOptions:\n\toperation\n\t\tDesc: Filter the traces that include this operation\n\t\tEx: operation=server.AddPets()\n\tstart\n\t\tDesc: Filter the trace by when in the past the trace started\n\t\tEx: start=01/02/2021-15:04:05\n\tend:\n\t\tDesc: Filter the trace by when the trace ends\n\t\tEx: end=01/02/2021-16:00:00\n\tlimit:\n\t\tDesc: Limit the number of traces returned (default is 20)\n\t\tEx: limit=5\n\ttags:\n\t\tDesc: Only include traces with these tags\n\t\tEx: tags=[tag,tag2]\n\t\tNote: no spaces are allowed in the tag list\n`,\n\n\t\"show trace\": `\nshow trace <trace id>\nEx: show trace 17b4f65b0d9f038e2a7bc5ea84309af2\n\nshow trace returns information about a particular Open Telemetry trace. \nThis command has no options.\n`,\n\n\t\"change sampling\": `\nchange sampling <type> <required value for type>\nEx: change sampling float .1\n\nSampling types:\n\tnever\n\t\tDesc: Never sample unless another service or the RPC requests a trace\n\talways\n\t\tDesc: Sample very incoming RPC\n\tfloat\n\t\tDesc: Sample at a specific rate\n\t\tRequired arg:\n\t\t\t<float>: Must be > 0 and <= 1\n\t\t\tEx: change sampling float .1\n`,\n\n\t\"show logs\": `\nshow logs <trace id>\nEx: show logs 17b4f65b0d9f038e2a7bc5ea84309af2\n\nshow logs returns all logs contained in a Open Telemetry trace. \nThis command has no options.\n`,\n}\n"
  },
  {
    "path": "chapter/11/chatbot/slack.manifest",
    "content": "display_information:\n  name: PetStore\nfeatures:\n  app_home:\n    home_tab_enabled: true\n    messages_tab_enabled: false\n    messages_tab_read_only_enabled: false\n  bot_user:\n    display_name: PetStore\n    always_online: false\noauth_config:\n  scopes:\n    bot:\n      - app_mentions:read\n      - channels:history\n      - channels:join\n      - chat:write\n      - chat:write.public\n      - commands\n      - files:write\n      - groups:read\n      - incoming-webhook\n      - links:write\n      - usergroups:read\n      - users.profile:read\n      - users:read\n      - chat:write.customize\n      - im:read\nsettings:\n  event_subscriptions:\n    bot_events:\n      - app_mention\n  interactivity:\n    is_enabled: true\n  org_deploy_enabled: false\n  socket_mode_enabled: true\n  token_rotation_enabled: false\n"
  },
  {
    "path": "chapter/11/docker-compose.yaml",
    "content": "version: \"2\"\nservices:\n\n  # Jaeger\n  jaeger-all-in-one:\n    image: jaegertracing/all-in-one:latest\n    ports:\n      - \"16685:16685\" # Query service port\n      - \"16686:16686\"\n      - \"14268:14268\"\n      - \"14250:14250\" \n\n  # Collector\n  otel-collector:\n    image: ${OTELCOL_IMG}\n    command: [\"--config=/etc/otel-collector-config.yaml\", \"${OTELCOL_ARGS}\"]\n    volumes:\n      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml\n    ports:\n      - \"1888:1888\"   # pprof extension\n      - \"13133:13133\" # health_check extension\n      - \"4317:4317\"        # OTLP gRPC receiver\n      - \"55670:55679\" # zpages extension\n    depends_on:\n      - jaeger-all-in-one\n\n  prometheus:\n    container_name: prometheus\n    image: prom/prometheus:latest\n    volumes:\n      - ./prometheus.yaml:/etc/prometheus/prometheus.yml\n    ports:\n      - \"9090:9090\"\n\n  petstore:\n    build:\n      dockerfile: Dockerfile\n      context: ./petstore\n    environment:\n      - OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317\n    ports:\n      - \"6742:6742\"\n    depends_on:\n      - otel-collector\n      - prometheus\n\n  ops:\n    build:\n      dockerfile: Dockerfile\n      context: ./\n    environment:\n      - OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317\n    ports:\n      - \"7000:7000\"\n    depends_on:\n      - petstore\n\n  petstore-client-demo:\n    build:\n      dockerfile: ./client/demo/Dockerfile\n      context: ./petstore\n    environment:\n      - OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317\n    depends_on:\n      - petstore\n"
  },
  {
    "path": "chapter/11/ops/README.md",
    "content": "# Ops Service\n\nThe Ops service provides API access to various operational information that we want to allow various tool access to. While this could be built into the chatbot.go binary that implements the ChatOps interface, this abstraction allows us to have multiple programs that can access these API calls. In addition, if we want to migrate to another chat service instead of Slack (like Microsoft Teams), we can easily do so without impacting users during a migration.\n\n## Basic Architecture\n\nThis is your standard gRPC service with a nice Go client ready made to access the service. We stick the important parts in `internal` to keep anyone from using the packages that are just for the service. `proto/` contains the protocol buffer messages we have for the client/server communication.\n\nThe file directory layout is as follows (with some highlighted files, but not all files):\n\n```\n├── client\n│   └── client.go\n├── internal\n│   ├── jaeger\n│   │   └── client\n│   │       ├── client.go\n│   │       └── test\n│   ├── prom\n│   │   └── prom.go\n│   └── server\n│       └── server.go\n├── ops.go\n└── proto\n    ├── jaeger\n    │   ├── model\n    ├── ops.pb.go\n    ├── ops.proto\n    └── ops_grpc.pb.go\n```\n\n* `ops.go` is the main file for the Ops service\n* `client/ provides` a client library for accessing our Ops service using Go\n* `internal/jaeger` provides a client wrapper for accessing Jaeger and some end to end testing\n* `internal/prom` provides a client wrapper for accessing prometheus\n* `proto/` contains protocol buffer messages and services for accessing the Ops service via gRPC\n\t* `proto/jaeger` provides various protocol buffers required to access Jaeger\n"
  },
  {
    "path": "chapter/11/ops/client/client.go",
    "content": "package client\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"google.golang.org/grpc\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/11/ops/proto\"\n\tmpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/11/ops/proto/jaeger/model\"\n)\n\n// Ops is a client for interacting with the Ops service.\ntype Ops struct {\n\tclient pb.OpsClient\n\tconn   *grpc.ClientConn\n}\n\n// New is the constructor for Client. addr is the server's [host]:[port].\nfunc New(addr string) (*Ops, error) {\n\tconn, err := grpc.Dial(addr, grpc.WithInsecure())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Ops{\n\t\tclient: pb.NewOpsClient(conn),\n\t\tconn:   conn,\n\t}, nil\n}\n\ntype callOptions struct {\n\tl *listTracesOpts\n\ta *alertsOpts\n}\n\ntype listTracesOpts struct {\n\toperation string\n\ttags      []string\n\tstart     time.Time\n\tend       time.Time\n\tlimit     int32\n}\n\ntype alertsOpts struct {\n\tlabels   []string\n\tactiveAt time.Time\n\tstates   []string\n}\n\nfunc (l *listTracesOpts) defaults() {\n\tl.limit = 10\n\tl.end = time.Now().Add(5 * time.Second)\n}\n\n// CallOption is an option for method.\ntype CallOption func(o *callOptions) error\n\n// WithStart sets the minimum time a trace had to start in order to be included.\nfunc WithStart(t time.Time) CallOption {\n\treturn func(o *callOptions) error {\n\t\tif o.l == nil {\n\t\t\treturn fmt.Errorf(\"WithStart can only be used on ListTraces()\")\n\t\t}\n\t\to.l.start = t\n\t\treturn nil\n\t}\n}\n\n// Withend sets the maximum time a trace had to start in order to be included.\nfunc WithEnd(t time.Time) CallOption {\n\treturn func(o *callOptions) error {\n\t\tif o.l == nil {\n\t\t\treturn fmt.Errorf(\"WithEnd can only be used on ListTraces()\")\n\t\t}\n\t\to.l.end = t\n\t\treturn nil\n\t}\n}\n\n// WithLimt sets the maximum amount of return values. When using Cassandra, this is\n// not a limit, it is some weird value where increasing it can increase how deep\n// cassandra searches for traces. In that case, setting 20 might return 10 results\n// but setting 100 might return 20.\nfunc WithLimit(i int32) CallOption {\n\treturn func(o *callOptions) error {\n\t\tif o.l == nil {\n\t\t\treturn fmt.Errorf(\"WithLimit can only be used on ListTraces()\")\n\t\t}\n\t\to.l.limit = i\n\t\treturn nil\n\t}\n}\n\n// WithOperation restricts traces to ones that have this operation.\nfunc WithOperation(s string) CallOption {\n\treturn func(o *callOptions) error {\n\t\tif o.l == nil {\n\t\t\treturn fmt.Errorf(\"WithOperation can only be used on ListTraces()\")\n\t\t}\n\t\to.l.operation = s\n\t\treturn nil\n\t}\n}\n\n// WithTags restricts resuts to ones that have all these tags.\nfunc WithTags(tags []string) CallOption {\n\treturn func(o *callOptions) error {\n\t\tif o.l == nil {\n\t\t\treturn fmt.Errorf(\"WithTags can only be used on ListTraces()\")\n\t\t}\n\t\to.l.tags = tags\n\t\treturn nil\n\t}\n}\n\n// TraceItem details information on a trace.\ntype TraceItem struct {\n\t// ID is the ID of the trace in hex form.\n\tID string\n\t// Start is the start time of the trace.\n\tStart time.Time\n}\n\n// ListTraces lists traces for the Petstore. By default it pulls the latest 10 items.\nfunc (o *Ops) ListTraces(ctx context.Context, options ...CallOption) ([]TraceItem, error) {\n\topts := callOptions{l: &listTracesOpts{}}\n\topts.l.defaults()\n\tfor _, o := range options {\n\t\to(&opts)\n\t}\n\n\treq := &pb.ListTracesReq{\n\t\tService:     \"petstore\",\n\t\tOperation:   opts.l.operation,\n\t\tTags:        opts.l.tags,\n\t\tStart:       opts.l.start.UnixNano(),\n\t\tEnd:         opts.l.end.UnixNano(),\n\t\tSearchDepth: opts.l.limit,\n\t}\n\n\tresp, err := o.client.ListTraces(ctx, req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\titems := make([]TraceItem, 0, len(resp.Traces))\n\tfor _, ti := range resp.Traces {\n\t\titems = append(items, TraceItem{ID: ti.Id, Start: time.Unix(0, ti.Start)})\n\t}\n\n\treturn items, nil\n}\n\ntype TraceData struct {\n\tID         string\n\tOperations []string\n\tErrors     []string\n\tTags       []string\n\tDuration   time.Time\n}\n\n// ShowTrace returns the Jaeger URL that is going to have the trace.\nfunc (o *Ops) ShowTrace(ctx context.Context, id string) (*pb.ShowTraceResp, error) {\n\tresp, err := o.client.ShowTrace(ctx, &pb.ShowTraceReq{Id: id})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn resp, nil\n}\n\ntype Log struct {\n\tTime  time.Time\n\tKey   string\n\tValue string\n}\n\nfunc (o *Ops) ShowLogs(ctx context.Context, id string) ([]Log, error) {\n\tresp, err := o.client.ShowLogs(ctx, &pb.ShowLogsReq{Id: id})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif resp.Id == \"\" {\n\t\treturn nil, fmt.Errorf(\"no trace with ID(%s) was found\", id)\n\t}\n\tlogs := make([]Log, 0, len(resp.Logs))\n\tfor _, l := range resp.Logs {\n\t\tt := l.Timestamp.AsTime().UTC()\n\t\tvar v string\n\t\tfor _, f := range l.Fields {\n\t\t\tswitch f.VType {\n\t\t\tcase mpb.ValueType_BINARY:\n\t\t\t\tv = string(f.VBinary)\n\t\t\tcase mpb.ValueType_BOOL:\n\t\t\t\tif f.VBool {\n\t\t\t\t\tv = \"true\"\n\t\t\t\t} else {\n\t\t\t\t\tv = \"false\"\n\t\t\t\t}\n\t\t\tcase mpb.ValueType_FLOAT64:\n\t\t\t\tv = strconv.FormatFloat(f.VFloat64, 'e', 2, 64)\n\t\t\tcase mpb.ValueType_INT64:\n\t\t\t\tv = strconv.FormatInt(f.VInt64, 10)\n\t\t\tcase mpb.ValueType_STRING:\n\t\t\t\tv = f.VStr\n\t\t\tdefault:\n\t\t\t\tv = fmt.Sprintf(\"unsupported type: %T\", f.VType)\n\t\t\t}\n\t\t\tlogs = append(logs, Log{Time: t, Key: f.Key, Value: v})\n\t\t}\n\t}\n\treturn logs, nil\n}\n\n// ChangeSampling is used to change the sampling rate of the service.\nfunc (o *Ops) ChangeSampling(ctx context.Context, sampler *pb.ChangeSamplingReq) error {\n\t_, err := o.client.ChangeSampling(ctx, sampler)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// DeployedVersion will return the deployed version of the application according\n// to Prometheus.\nfunc (o *Ops) DeployedVersion(ctx context.Context) (string, error) {\n\tresp, err := o.client.DeployedVersion(ctx, &pb.DeployedVersionReq{})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn resp.Version, nil\n}\n\n// WithLabels restricts alerts to ones that have all these labels.\nfunc WithLabels(labels []string) CallOption {\n\treturn func(o *callOptions) error {\n\t\tif o.a == nil {\n\t\t\treturn fmt.Errorf(\"WithLabels can only be used on Alerts()\")\n\t\t}\n\t\to.a.labels = labels\n\t\treturn nil\n\t}\n}\n\n// WithActiveAt restrics alerts to ones from this time to now.\nfunc WithActiveAt(t time.Time) CallOption {\n\treturn func(o *callOptions) error {\n\t\tif o.a == nil {\n\t\t\treturn fmt.Errorf(\"WithActiveAt can only be used on Alerts()\")\n\t\t}\n\t\to.a.activeAt = t\n\t\treturn nil\n\t}\n}\n\n// WithStates restrics alerts to ones that have one of these states.\nfunc WithStates(states []string) CallOption {\n\treturn func(o *callOptions) error {\n\t\tif o.a == nil {\n\t\t\treturn fmt.Errorf(\"WithStates can only be used on Alerts()\")\n\t\t}\n\t\to.a.states = states\n\t\treturn nil\n\t}\n}\n\n// Alert represents a Prometheus alert.\ntype Alert struct {\n\t// State is the state of the alert.\n\tState string\n\t// Value is the value of the alert.\n\tValue string\n\t// ActiveAt was when the alert started.\n\tActiveAt time.Time\n}\n\nfunc (a *Alert) fromProto(p *pb.Alert) {\n\ta.State = p.State\n\ta.Value = p.Value\n\ta.ActiveAt = time.Unix(0, p.ActiveAt)\n}\n\n// Alerts returns a list of Prometheus alerts that are firing.\nfunc (o *Ops) Alerts(ctx context.Context, options ...CallOption) ([]Alert, error) {\n\topts := callOptions{a: &alertsOpts{}}\n\tfor _, o := range options {\n\t\to(&opts)\n\t}\n\n\treq := &pb.AlertsReq{\n\t\tLabels:   opts.a.labels,\n\t\tActiveAt: opts.a.activeAt.UnixNano(),\n\t\tStates:   opts.a.states,\n\t}\n\n\tresp, err := o.client.Alerts(ctx, req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\talerts := make([]Alert, 0, len(resp.Alerts))\n\tfor _, p := range resp.Alerts {\n\t\ta := Alert{}\n\t\ta.fromProto(p)\n\t\talerts = append(alerts, a)\n\t}\n\treturn alerts, nil\n}\n"
  },
  {
    "path": "chapter/11/ops/internal/jaeger/client/client.go",
    "content": "// Package client provides a Jaegar client for grabbing traces from the Jaegar service.\n// It is a wrapper around the undocumented Jaegar gRPC client.\npackage client\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n\n\totelTrace \"go.opentelemetry.io/otel/trace\"\n\t\"google.golang.org/grpc\"\n\tduration \"google.golang.org/protobuf/types/known/durationpb\"\n\ttimestamp \"google.golang.org/protobuf/types/known/timestamppb\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/11/ops/proto/jaeger\"\n\tmpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/11/ops/proto/jaeger/model\"\n)\n\nvar (\n\t// ErrNotFound indicates that no trace with an ID was found.\n\tErrNotFound = errors.New(\"trace with that ID was not found\")\n)\n\n// Trace represents a OTEL trace that was stored in Jaegar.\ntype Trace struct {\n\t// ID is the identity of the trace.\n\tID string\n\t// Spans are the spans that make up the trace.\n\tSpans []Span\n\n\t// Err indicates if there was a error in the trace stream if the\n\t// Trace is being returned in a channel. If not, this will always be nil.\n\tErr error\n}\n\n// Span is a convienence wrapper around *mpb.Span.\ntype Span struct {\n\t*mpb.Span\n}\n\n// Proto returns the encapsulated proto. Remember that these are generated with\n// gogo proto and not Google/Buf.build proto engine.\nfunc (s Span) Proto() *mpb.Span {\n\treturn s.Span\n}\n\n// TraceID returns the converted human readable Trace ID.\nfunc (s Span) TraceID() string {\n\tif len(s.Span.TraceId) < 16 {\n\t\treturn \"\"\n\t}\n\t// This is a go 1.17 conversion of a slice to an array.\n\tt := (*otelTrace.TraceID)(s.Span.TraceId[0:16])\n\treturn t.String()\n}\n\n// SpanID returns the converted human readable Span ID.\nfunc (s Span) SpanID() string {\n\tif len(s.Span.SpanId) < 16 {\n\t\treturn \"\"\n\t}\n\tt := (*otelTrace.SpanID)(s.Span.SpanId[0:16])\n\treturn t.String()\n}\n\n// Jaeger provides a client for interacting with Jaeger to retrieve traces.\ntype Jaeger struct {\n\tclient pb.QueryServiceClient\n\tconn   *grpc.ClientConn\n\taddr   string\n}\n\n// New creates a new Jaeger client that connects to addr.\nfunc New(addr string) (*Jaeger, error) {\n\tconn, err := grpc.Dial(addr, grpc.WithInsecure())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Jaeger{\n\t\tclient: pb.NewQueryServiceClient(conn),\n\t\tconn:   conn,\n\t\taddr:   addr,\n\t}, nil\n}\n\n// Addr returns the address of the Jaeger server we are connected to.\nfunc (j *Jaeger) Addr() string {\n\treturn j.addr\n}\n\n// SearchParams are parameters used to filter a search for trace data.\ntype SearchParams struct {\n\t// Service is the name of the service you are querying. Required.\n\tService string\n\t// Operation is the name of the operation you want to filter by.\n\tOperation string\n\t// Tag values you want to filter by.\n\tTags []string\n\t// Start is the lower bounds (inclusive) that a trace can start at.\n\tStart time.Time\n\t// End is the uppper bounds (exclusive) a trace can end at.\n\tEnd time.Time\n\t// DurationMin is the minimum duration a trace (inclusive) must have.\n\tDurationMin time.Duration\n\t// DurationMax is the maximum duration a trace (exclusive) can have.\n\tDurationMax time.Duration\n\t// SearchDepth is a quirky setting. It is kinda tells the data store how hard to search.\n\t// On data stores like Cassandra, settting this to a higher number will cause it\n\t// to search deeper in its trees for the data. So a setting of 20 might get 8 results, but 200 will get\n\t// 15. This looks to have only a limit like effect on other storage systems. This defaults to 200.\n\tSearchDepth int32\n}\n\nfunc (s SearchParams) validate() error {\n\tif s.Service == \"\" {\n\t\treturn errors.New(\"Service field must not be an empty string\")\n\t}\n\treturn nil\n}\n\nfunc (s SearchParams) proto() *pb.FindTracesRequest {\n\tif s.SearchDepth == 0 {\n\t\ts.SearchDepth = 20\n\t}\n\tvar t map[string]string\n\tif len(s.Tags) > 0 {\n\t\tt = make(map[string]string, len(s.Tags))\n\t\tfor _, tag := range s.Tags {\n\t\t\tt[tag] = \"\"\n\t\t}\n\t}\n\n\treturn &pb.FindTracesRequest{\n\t\tQuery: &pb.TraceQueryParameters{\n\t\t\tServiceName:   s.Service,\n\t\t\tOperationName: s.Operation,\n\t\t\tTags:          t,\n\t\t\tStartTimeMin:  timestamp.New(s.Start),\n\t\t\tStartTimeMax:  timestamp.New(s.End),\n\t\t\tDurationMin:   duration.New(s.DurationMin),\n\t\t\tDurationMax:   duration.New(s.DurationMax),\n\t\t\tSearchDepth:   s.SearchDepth,\n\t\t},\n\t}\n}\n\n// Search searches Jaeger for traces that match the SearchParams. Each result is the set of spans that make up a trace.\nfunc (j *Jaeger) Search(ctx context.Context, params SearchParams) (chan Trace, error) {\n\tif err := params.validate(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tstream, err := j.client.FindTraces(ctx, params.proto())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Traces come in chunks of spans. So we look at the chunks that come in and combine spans with the same IDs into traces.\n\treturn unwind(ctx, stream), nil\n}\n\n// Trace allows retreival of a specific trace from Jaegar by its ID.\nfunc (j *Jaeger) Trace(ctx context.Context, id string) (Trace, error) {\n\ttid, err := otelTrace.TraceIDFromHex(id)\n\tif err != nil {\n\t\treturn Trace{}, fmt.Errorf(\"trace ID was invalid: %w\", err)\n\t}\n\n\treq := &pb.GetTraceRequest{\n\t\tTraceId: tid[0:16],\n\t}\n\n\tstream, err := j.client.GetTrace(ctx, req)\n\tif err != nil {\n\t\treturn Trace{}, err\n\t}\n\n\tch := unwind(ctx, stream)\n\ttraces := make([]Trace, 0, 1)\n\tfor trace := range ch {\n\t\ttraces = append(traces, trace)\n\t}\n\tswitch len(traces) {\n\tcase 0:\n\t\treturn Trace{}, ErrNotFound\n\tcase 1:\n\t\treturn traces[0], nil\n\t}\n\treturn Trace{}, fmt.Errorf(\"bug: received more that a single Trace\")\n}\n\ntype receiver interface {\n\tRecv() (*pb.SpansResponseChunk, error)\n\tgrpc.ClientStream\n}\n\n// unwind unwinds traces that come in chunks of spans. So we look at the chunks that come in and combine spans with the same IDs into traces.\nfunc unwind(ctx context.Context, stream receiver) chan Trace {\n\tch := make(chan Trace, 1)\n\tgo func() {\n\t\tdefer close(ch)\n\t\tvar lastTrace Trace\n\t\tfor {\n\t\t\tif ctx.Err() != nil {\n\t\t\t\tch <- Trace{Err: ctx.Err()}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tchunk, err := stream.Recv()\n\t\t\tif err == io.EOF {\n\t\t\t\tif lastTrace.ID != \"\" {\n\t\t\t\t\tch <- lastTrace\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\tch <- Trace{Err: err}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tspans := chunkToSpan(chunk)\n\t\t\tif len(spans) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif spans[0].TraceID() != lastTrace.ID {\n\t\t\t\tif lastTrace.ID != \"\" {\n\t\t\t\t\tch <- lastTrace\n\t\t\t\t}\n\t\t\t\tlastTrace = Trace{ID: spans[0].TraceID(), Spans: spans}\n\t\t\t} else {\n\t\t\t\tlastTrace.Spans = append(lastTrace.Spans, spans...)\n\t\t\t}\n\t\t}\n\t}()\n\treturn ch\n}\n\nfunc chunkToSpan(chunk *pb.SpansResponseChunk) []Span {\n\tif len(chunk.Spans) == 0 {\n\t\treturn nil\n\t}\n\tvar spans []Span\n\tfor _, s := range chunk.Spans {\n\t\tspans = append(spans, Span{Span: s})\n\t}\n\treturn spans\n}\n"
  },
  {
    "path": "chapter/11/ops/internal/jaeger/client/test/README.md",
    "content": "This provides an end to end test using docker to turn up Jaeger and an http server using Jaeger.\n\nWe kick off some traces using the client and then use our jaeger client to find the traces.\n\nThe test will kick off the docker-compose environment on its own.\n"
  },
  {
    "path": "chapter/11/ops/internal/jaeger/client/test/client/client.go",
    "content": "package client\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\"\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc\"\n\t\"go.opentelemetry.io/otel/propagation\"\n\t\"go.opentelemetry.io/otel/sdk/resource\"\n\tsdktrace \"go.opentelemetry.io/otel/sdk/trace\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.4.0\"\n\t\"go.opentelemetry.io/otel/trace\"\n\t\"google.golang.org/grpc\"\n)\n\n// NestedSpans is the number of nested spans to create. Ths is set to 2560, because for some\n// reason that is the maximum number of spans. That may be due to some byte limit or a fixed number.\n// I could find no documentation to indicate why, thought I didn't look throughthe source.\nconst NestedSpans = 2560\n\n// Initializes an OTLP exporter, and configures the corresponding trace providers.\nfunc initProvider() func() {\n\tctx := context.Background()\n\n\ttraceExp := initTracer(ctx, \"127.0.0.1:4317\")\n\tlog.Println(\"intTracer done\")\n\treturn func() {\n\t\tctx, cancel := context.WithTimeout(ctx, time.Second)\n\t\tdefer cancel()\n\t\tif err := traceExp.Shutdown(ctx); err != nil {\n\t\t\totel.Handle(err)\n\t\t}\n\t}\n}\n\nfunc initTracer(ctx context.Context, otelAgentAddr string) *otlptrace.Exporter {\n\ttraceClient := otlptracegrpc.NewClient(\n\t\totlptracegrpc.WithInsecure(),\n\t\totlptracegrpc.WithEndpoint(otelAgentAddr),\n\t\totlptracegrpc.WithDialOption(grpc.WithBlock(), grpc.WithTimeout(time.Second)))\n\ttraceExp, err := otlptrace.New(ctx, traceClient)\n\thandleErr(err, \"Failed to create the collector trace exporter\")\n\n\tres, err := resource.New(ctx,\n\t\tresource.WithFromEnv(),\n\t\tresource.WithProcess(),\n\t\tresource.WithTelemetrySDK(),\n\t\tresource.WithHost(),\n\t\tresource.WithAttributes(\n\t\t\t// the service name used to display traces in backends\n\t\t\tsemconv.ServiceNameKey.String(\"demo-client\"),\n\t\t),\n\t)\n\thandleErr(err, \"failed to create resource\")\n\n\tbsp := sdktrace.NewBatchSpanProcessor(traceExp)\n\ttracerProvider := sdktrace.NewTracerProvider(\n\t\tsdktrace.WithSampler(sdktrace.AlwaysSample()),\n\t\tsdktrace.WithResource(res),\n\t\tsdktrace.WithSpanProcessor(bsp),\n\t)\n\n\t// set global propagator to tracecontext (the default is no-op).\n\totel.SetTextMapPropagator(propagation.TraceContext{})\n\totel.SetTracerProvider(tracerProvider)\n\treturn traceExp\n}\n\nfunc handleErr(err error, message string) {\n\tif err != nil {\n\t\tlog.Fatalf(\"%s: %v\", message, err)\n\t}\n}\n\nvar once sync.Once\n\nvar Shutdown func() = func() {}\n\ntype HTTP struct {\n\taddr   string\n\tclient http.Client\n}\n\nfunc New(addr string) (*HTTP, error) {\n\tonce.Do(\n\t\tfunc() {\n\t\t\tlog.Println(\"before initProvider\")\n\t\t\tShutdown = initProvider()\n\t\t\tlog.Println(\"after initProvider\")\n\t\t},\n\t)\n\n\th := &HTTP{client: http.Client{Transport: otelhttp.NewTransport(http.DefaultTransport)}}\n\tlog.Println(\"after *HTTP client\")\n\treturn h, nil\n}\n\nfunc (h *HTTP) Call(ctx context.Context) (traceID string, err error) {\n\ttracer := otel.Tracer(\"demo-client-tracer\")\n\n\tctx, span := tracer.Start(ctx, \"ExecuteRequest\")\n\tdefer span.End()\n\n\tctx = h.makeNestedSpans(ctx, tracer)\n\th.makeRequest(ctx)\n\n\tlog.Println(\"trace says: \", span.SpanContext().TraceID().String())\n\tlog.Println(\"convert says: \", convertTraceID(span.SpanContext().TraceID().String()))\n\n\treturn span.SpanContext().TraceID().String(), nil\n}\n\nfunc (h *HTTP) makeRequest(ctx context.Context) error {\n\t// Make sure we pass the context to the request to avoid broken traces.\n\treq, err := http.NewRequestWithContext(ctx, \"GET\", h.addr, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// All requests made with this client will create spans.\n\tres, err := h.client.Do(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tres.Body.Close()\n\treturn nil\n}\n\nfunc (h *HTTP) makeNestedSpans(ctx context.Context, tracer trace.Tracer) context.Context {\n\tspans := []trace.Span{}\n\tfor i := 0; i < NestedSpans; i++ {\n\t\tvar span trace.Span\n\t\tctx, span = tracer.Start(ctx, uuid.New().String())\n\t\tspans = append(spans, span)\n\t}\n\tfor i := NestedSpans - 1; i > -1; i-- {\n\t\tspans[i].End()\n\t}\n\treturn ctx\n}\n\nfunc convertTraceID(id string) string {\n\tif len(id) < 16 {\n\t\treturn \"\"\n\t}\n\tif len(id) > 16 {\n\t\tid = id[16:]\n\t}\n\tintValue, err := strconv.ParseUint(id, 16, 64)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn strconv.FormatUint(intValue, 10)\n}\n"
  },
  {
    "path": "chapter/11/ops/internal/jaeger/client/test/docker-compose.yaml",
    "content": "version: \"2\"\nservices:\n\n  # Jaeger\n  jaeger-all-in-one:\n    image: jaegertracing/all-in-one:latest\n    ports:\n      - \"16685:16685\"\n      - \"16686:16686\"\n      - \"14268:14268\"\n      - \"14250:14250\"\n\n  # Collector\n  otel-collector:\n    image: ${OTELCOL_IMG}\n    command: [\"--config=/etc/otel-collector-config.yaml\", \"${OTELCOL_ARGS}\"]\n    volumes:\n      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml\n    ports:\n      - \"1888:1888\"   # pprof extension\n      - \"13133:13133\" # health_check extension\n      - \"4317:4317\"        # OTLP gRPC receiver\n      - \"55670:55679\" # zpages extension\n    depends_on:\n      - jaeger-all-in-one\n\n  demo-server:\n    build:\n      dockerfile: Dockerfile\n      context: ./server\n    environment:\n      - OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317\n    ports:\n      - \"7080:7080\"\n    depends_on:\n      - otel-collector\n"
  },
  {
    "path": "chapter/11/ops/internal/jaeger/client/test/etoe_test.go",
    "content": "package etoe\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"os/exec\"\n\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/ops/internal/jaeger/client\"\n\thttpClient \"github.com/PacktPublishing/Go-for-DevOps/chapter/11/ops/internal/jaeger/client/test/client\"\n)\n\nfunc TestTrace(t *testing.T) {\n\tstart := exec.Command(\"docker-compose\", \"up\", \"-d\")\n\tb, err := start.CombinedOutput()\n\tif err != nil {\n\t\tpanic(string(b))\n\t}\n\ttime.Sleep(5 * time.Second)\n\n\tend := exec.Command(\"docker-compose\", \"down\")\n\tdefer func() {\n\t\tb, err = end.CombinedOutput()\n\t\tif err != nil {\n\t\t\tpanic(string(b))\n\t\t}\n\t}()\n\n\tc, err := client.New(\"127.0.0.1:16685\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\th, err := httpClient.New(\"127.0.0.1:7080\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tids := []string{}\n\tfor i := 0; i < 10; i++ {\n\t\tcallCtx, callCancel := context.WithTimeout(context.Background(), 2*time.Second)\n\t\tid, err := h.Call(callCtx)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tcallCancel()\n\t\tids = append(ids, id)\n\t}\n\n\tlog.Println(\"sleeping to let trace get exported\")\n\ttime.Sleep(20 * time.Second)\n\n\ttraceCtx, traceCancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer traceCancel()\n\n\ttrace, err := c.Trace(traceCtx, ids[0])\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tif len(trace.Spans) != httpClient.NestedSpans {\n\t\tt.Errorf(\"TestTrace(number of Spans): got %d, want %d\", len(trace.Spans), httpClient.NestedSpans)\n\t}\n\n\tfor _, id := range ids {\n\t\t_, err := c.Trace(traceCtx, id)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\t// So, I don't know what the deal is.\n\t/*\n\t\tch, err := c.Search(traceCtx, client.SearchParams{Service: \"demo-client\", SearchDepth: 100})\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tfound := []string{}\n\t\tfor e := range ch {\n\t\t\tfound = append(found, e.ID)\n\t\t}\n\n\t\tsort.Strings(found)\n\t\tsort.Strings(ids)\n\n\t\tif len(ids) != len(found){\n\t\t\tt.Fatalf(\"TestTrace(Search): number of IDs: got %d, want %d\", len(found), len(ids))\n\t\t}\n\n\t\tfor i, id := range ids {\n\t\t\tif id != found[i] {\n\t\t\t\tt.Fatalf(\"TestTrace(Search): trace ids[%d]: got %s, want %s\", i, found[i], id)\n\t\t\t}\n\t\t}\n\t*/\n}\n"
  },
  {
    "path": "chapter/11/ops/internal/jaeger/client/test/otel-collector-config.yaml",
    "content": "receivers:\n  otlp:\n    protocols:\n      grpc:\n\nexporters:\n  jaeger:\n    endpoint: jaeger-all-in-one:14250\n    tls:\n      insecure: true\n\nprocessors:\n  batch:\n\nservice:\n  pipelines:\n    traces:\n      receivers: [otlp]\n      processors: [batch]\n      exporters: [jaeger]\n\n"
  },
  {
    "path": "chapter/11/ops/internal/jaeger/client/test/server/Dockerfile",
    "content": "FROM golang:1.17\nCOPY . /usr/src/server/\nWORKDIR /usr/src/server/\nRUN go env -w GOPROXY=direct\nRUN go install ./main.go\nCMD [\"/go/bin/main\"]\n"
  },
  {
    "path": "chapter/11/ops/internal/jaeger/client/test/server/docker-compose.yaml",
    "content": "version: \"2\"\nservices:\n\n  # Jaeger\n  jaeger-all-in-one:\n    image: jaegertracing/all-in-one:latest\n    ports:\n      - \"16686:16686\"\n      - \"14268\"\n      - \"14250\"\n\n  # Collector\n  otel-collector:\n    image: ${OTELCOL_IMG}\n    command: [\"--config=/etc/otel-collector-config.yaml\", \"${OTELCOL_ARGS}\"]\n    volumes:\n      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml\n    ports:\n      - \"1888:1888\"   # pprof extension\n      - \"13133:13133\" # health_check extension\n      - \"4317\"        # OTLP gRPC receiver\n      - \"55670:55679\" # zpages extension\n    depends_on:\n      - jaeger-all-in-one\n\n  demo-client:\n    build:\n      dockerfile: Dockerfile\n      context: ./client\n    environment:\n      - OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317\n      - DEMO_SERVER_ENDPOINT=http://demo-server:7080/hello\n    depends_on:\n      - demo-server\n\n  demo-server:\n    build:\n      dockerfile: Dockerfile\n      context: ./server\n    environment:\n      - OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317\n    ports:\n      - \"7080\"\n    depends_on:\n      - otel-collector\n"
  },
  {
    "path": "chapter/11/ops/internal/jaeger/client/test/server/go.mod",
    "content": "module github.com/PacktPublishing/Go-for-DevOps/chapter/9/tracing/demo/server\n\ngo 1.17\n\nrequire (\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0\n\tgo.opentelemetry.io/otel v1.3.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0\n\tgo.opentelemetry.io/otel/sdk v1.3.0\n\tgo.opentelemetry.io/otel/trace v1.3.0\n\tgoogle.golang.org/grpc v1.43.0\n)\n\nrequire (\n\tgithub.com/cenkalti/backoff/v4 v4.1.2 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/felixge/httpsnoop v1.0.2 // indirect\n\tgithub.com/go-logr/logr v1.2.1 // indirect\n\tgithub.com/go-logr/stdr v1.2.0 // indirect\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgithub.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 // indirect\n\tgo.opentelemetry.io/otel/internal/metric v0.26.0 // indirect\n\tgo.opentelemetry.io/otel/metric v0.26.0 // indirect\n\tgo.opentelemetry.io/proto/otlp v0.11.0 // indirect\n\tgolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect\n\tgolang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect\n\tgolang.org/x/text v0.3.3 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece // indirect\n\tgoogle.golang.org/protobuf v1.27.1 // indirect\n)\n"
  },
  {
    "path": "chapter/11/ops/internal/jaeger/client/test/server/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=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=\ngithub.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\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/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\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/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.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=\ngithub.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=\ngithub.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=\ngithub.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/protobuf v1.2.0/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.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/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.5.0/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 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0 h1:hpEoMBvKLC6CqFZogJypr9IHwwSNF3ayEkNzD502QAM=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0/go.mod h1:Ihno+mNBfZlT0Qot3XyRTdZ/9U/Cg2Pfgj75DTdIfq4=\ngo.opentelemetry.io/otel v1.3.0 h1:APxLf0eiBwLl+SOXiJJCVYzA1OOJNyAoV8C5RNRyy7Y=\ngo.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 h1:R/OBkMoGgfy2fLhs2QhkCI1w4HLEQX92GCcJB6SSdNk=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0 h1:giGm8w67Ja7amYNfYMdme7xSp2pIxThWopw8+QP51Yk=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0 h1:VQbUHoJqytHHSJ1OZodPH9tvZZSVzUHjPHpkO85sT6k=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY=\ngo.opentelemetry.io/otel/internal/metric v0.26.0 h1:dlrvawyd/A+X8Jp0EBT4wWEe4k5avYaXsXrBr4dbfnY=\ngo.opentelemetry.io/otel/internal/metric v0.26.0/go.mod h1:CbBP6AxKynRs3QCbhklyLUtpfzbqCLiafV9oY2Zj1Jk=\ngo.opentelemetry.io/otel/metric v0.26.0 h1:VaPYBTvA13h/FsiWfxa3yZnZEm15BhStD8JZQSA773M=\ngo.opentelemetry.io/otel/metric v0.26.0/go.mod h1:c6YL0fhRo4YVoNs6GoByzUgBp36hBL523rECoZA5UWg=\ngo.opentelemetry.io/otel/sdk v1.3.0 h1:3278edCoH89MEJ0Ky8WQXVmDQv3FX4ZJ3Pp+9fJreAI=\ngo.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=\ngo.opentelemetry.io/otel/trace v1.3.0 h1:doy8Hzb1RJ+I3yFhtDmwNc7tIyw1tNMOIsyPzp1NOGY=\ngo.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.opentelemetry.io/proto/otlp v0.11.0 h1:cLDgIBTf4lLOlztkhzAEdQsJ4Lj+i5Wc9k6Nn0K1VyU=\ngo.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ=\ngo.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=\ngo.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\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-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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\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-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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\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=\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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece h1:1YM0uhfumvoDu9sx8+RyWwTI63zoCQvI23IYFRlvte0=\ngoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM=\ngoogle.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\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/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/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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\n"
  },
  {
    "path": "chapter/11/ops/internal/jaeger/client/test/server/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\"\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc\"\n\t\"go.opentelemetry.io/otel/propagation\"\n\t\"go.opentelemetry.io/otel/sdk/resource\"\n\tsdktrace \"go.opentelemetry.io/otel/sdk/trace\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.4.0\"\n\t\"go.opentelemetry.io/otel/trace\"\n\t\"google.golang.org/grpc\"\n)\n\n// Initializes an OTLP exporter, and configures the corresponding trace providers.\nfunc initProvider() func() {\n\tctx := context.Background()\n\n\totelAgentAddr, ok := os.LookupEnv(\"OTEL_EXPORTER_OTLP_ENDPOINT\")\n\tif !ok {\n\t\totelAgentAddr = \"0.0.0.0:4317\"\n\t}\n\n\ttraceExp := initTracer(ctx, otelAgentAddr)\n\treturn func() {\n\t\tcxt, cancel := context.WithTimeout(ctx, time.Second)\n\t\tdefer cancel()\n\t\tif err := traceExp.Shutdown(cxt); err != nil {\n\t\t\totel.Handle(err)\n\t\t}\n\t}\n}\n\nfunc initTracer(ctx context.Context, otelAgentAddr string) *otlptrace.Exporter {\n\ttraceClient := otlptracegrpc.NewClient(\n\t\totlptracegrpc.WithInsecure(),\n\t\totlptracegrpc.WithEndpoint(otelAgentAddr),\n\t\totlptracegrpc.WithDialOption(grpc.WithBlock()))\n\ttraceExp, err := otlptrace.New(ctx, traceClient)\n\thandleErr(err, \"Failed to create the collector trace exporter\")\n\n\tres, err := resource.New(ctx,\n\t\tresource.WithFromEnv(),\n\t\tresource.WithProcess(),\n\t\tresource.WithTelemetrySDK(),\n\t\tresource.WithHost(),\n\t\tresource.WithAttributes(\n\t\t\t// the service name used to display traces in backends\n\t\t\tsemconv.ServiceNameKey.String(\"demo-server\"),\n\t\t),\n\t)\n\thandleErr(err, \"failed to create resource\")\n\n\tbsp := sdktrace.NewBatchSpanProcessor(traceExp)\n\ttracerProvider := sdktrace.NewTracerProvider(\n\t\tsdktrace.WithSampler(sdktrace.AlwaysSample()),\n\t\tsdktrace.WithResource(res),\n\t\tsdktrace.WithSpanProcessor(bsp),\n\t)\n\n\t// set global propagator to tracecontext (the default is no-op).\n\totel.SetTextMapPropagator(propagation.TraceContext{})\n\totel.SetTracerProvider(tracerProvider)\n\treturn traceExp\n}\n\nfunc handleErr(err error, message string) {\n\tif err != nil {\n\t\tlog.Fatalf(\"%s: %v\", message, err)\n\t}\n}\n\nfunc main() {\n\tshutdown := initProvider()\n\tdefer shutdown()\n\n\tserverAttribute := attribute.String(\"server-attribute\", \"foo\")\n\t// create a handler wrapped in OpenTelemetry instrumentation\n\thandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {\n\t\tctx := req.Context()\n\t\tspan := trace.SpanFromContext(ctx)\n\t\tspan.SetAttributes(serverAttribute)\n\t\tw.Write([]byte(\"Hello World\"))\n\t\tlog.Println(\"got called\")\n\t})\n\twrappedHandler := otelhttp.NewHandler(handler, \"/hello\")\n\n\t// serve up the wrapped handler\n\thttp.Handle(\"/hello\", wrappedHandler)\n\tlog.Println(\"server started\")\n\thttp.ListenAndServe(\":7080\", nil)\n}\n"
  },
  {
    "path": "chapter/11/ops/internal/jaeger/client/test/server/otel-collector-config.yaml",
    "content": "receivers:\n  otlp:\n    protocols:\n      grpc:\n\nexporters:\n  jaeger:\n    endpoint: jaeger-all-in-one:14250\n    tls:\n      insecure: true\n\nprocessors:\n  batch:\n\nservice:\n  pipelines:\n    traces:\n      receivers: [otlp]\n      processors: [batch]\n      exporters: [jaeger]\n\n"
  },
  {
    "path": "chapter/11/ops/internal/jaeger/client/test/server/readme.md",
    "content": "# Tracing with OpenTelemetry and Jaeger\n\nTODO: fill in the walk through\n\n## Running this example\n- `docker-compose up -d`\n- Once started the client application will periodically send requests to the server. Distributed traces will be collected for the requests and responses, then exported for analysis in Jaeger. To view the traces in Jaeger, open http://localhost:16686.\n\nIf you see something like:\n```bash\ndocker-compose up -d\nTraceback (most recent call last):\n  File \"urllib3/connectionpool.py\", line 670, in urlopen\n  File \"urllib3/connectionpool.py\", line 392, in _make_request\n```\nThis indicates that you aren't running docker. Make sure you have docker installed and it is running.\n\n## Tearing down this example\n- `docker-compose down`\n\n## Influences / Credit\nThe code in this demo was heavily influenced from the example application in https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/examples/demo\nwhich carries the following license.\n```\n// Copyright The OpenTelemetry Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//       http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n```\n\nSee also: [OTEL License](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/10cfdaac1387b4df7a525c3050ce18ec8f8068be/LICENSE\n"
  },
  {
    "path": "chapter/11/ops/internal/prom/prom.go",
    "content": "package prom\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"regexp\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/prometheus/client_golang/api\"\n\tv1 \"github.com/prometheus/client_golang/api/prometheus/v1\"\n\t\"github.com/prometheus/common/model\"\n\t//\"github.com/prometheus/common/config\"\n)\n\n// Client is a wrapper around the prometheus Go client for our needs.\ntype Client struct {\n\tclient v1.API\n}\n\n// New creates a new client connecting to the HTTP address provided. This should be in the form of http[s]://[domain,host,ip]:[port] .\nfunc New(httpAddr string) (*Client, error) {\n\tclient, err := api.NewClient(\n\t\tapi.Config{\n\t\t\tAddress: httpAddr,\n\t\t},\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Client{client: v1.NewAPI(client)}, nil\n}\n\n// Metric returns the metrics current value.\nfunc (c *Client) Metric(ctx context.Context, metric string) (model.Value, v1.Warnings, error) {\n\treturn c.client.Query(ctx, metric, time.Now())\n}\n\n// Range does a query within a range of time and returns the result. A query might be something like:\n// \"rate(prometheus_tsdb_head_samples_appended_total[5m])\".\nfunc (c *Client) Range(ctx context.Context, query string, r v1.Range) (model.Value, v1.Warnings, error) {\n\treturn c.client.QueryRange(ctx, query, r)\n}\n\n// AlertFilter represents a filter you can use to filter out alerts.\ntype AlertFilter struct {\n\t// Labels filters alerts by attached labels. If only a key is provided, we only match on the key.\n\t// Values that start with \"regexp/\" are regexp compiled and then matched against a value stored at that key.\n\t// Defaults to all labels.\n\tLabels       map[string]string\n\tlabelRegexes map[string]*regexp.Regexp\n\t// ActiveAt filters out any alerts that are before this time. Defaults to all alerts.\n\tActiveAt time.Time\n\t// States filters alerts to only ones in these states. Defaults to all states.\n\tStates []string\n\tstates map[string]bool\n\t// Value filters out all values that don't match the regex.\n\tValue *regexp.Regexp\n\n\tcompiled bool\n}\n\n// Compile compiles the AlertFilter. If this is a one off query, no need to do this. If you plan\n// to reuse the filter, this will increase the speed. Changing a filter after Compile() is called\n// will not give you the desired result, create a new filter instead.\nfunc (a *AlertFilter) Compile() error {\n\tif a.compiled {\n\t\treturn nil\n\t}\n\tif len(a.Labels) > 0 {\n\t\tregexes := make(map[string]*regexp.Regexp, len(a.Labels))\n\t\tfor k, v := range a.Labels {\n\t\t\tif strings.TrimSpace(k) == \"\" {\n\t\t\t\treturn fmt.Errorf(\"cannot have a empty string label key\")\n\t\t\t}\n\t\t\tif strings.HasPrefix(v, \"regexp/\") {\n\t\t\t\tsp := strings.Split(v, \"regexp/\")\n\t\t\t\tif len(sp) == 1 {\n\t\t\t\t\treturn fmt.Errorf(\"label value with regexp/ must have more content\")\n\t\t\t\t}\n\t\t\t\tif len(sp) > 2 {\n\t\t\t\t\treturn fmt.Errorf(\"regexp/ can only be at the beginning of a label value\")\n\t\t\t\t}\n\t\t\t\tr, err := regexp.Compile(sp[1])\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"label with value(%s) cannot be regexp compiled: %w\", v, err)\n\t\t\t\t}\n\t\t\t\tregexes[k] = r\n\t\t\t}\n\t\t}\n\t\ta.labelRegexes = regexes\n\t}\n\tif len(a.States) > 0 {\n\t\ta.states = map[string]bool{}\n\t\tfor _, s := range a.States {\n\t\t\ta.states[s] = true\n\t\t}\n\t}\n\ta.compiled = true\n\treturn nil\n}\n\nfunc (a *AlertFilter) filter(items []v1.Alert) (chan v1.Alert, error) {\n\tif !a.compiled {\n\t\tif err := a.Compile(); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tch := make(chan v1.Alert, runtime.NumCPU())\n\tlimit := make(chan struct{}, runtime.NumCPU())\n\twg := sync.WaitGroup{}\n\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tfor _, item := range items {\n\t\t\titem := item\n\t\t\tlimit <- struct{}{}\n\t\t\twg.Add(1)\n\t\t\tfunc() {\n\t\t\t\tdefer wg.Done()\n\t\t\t\tdefer func() { <-limit }()\n\t\t\t\tgo a.pipeline(item, ch)\n\t\t\t}()\n\t\t}\n\t}()\n\n\tgo func() {\n\t\twg.Wait()\n\t\tclose(ch)\n\t}()\n\treturn ch, nil\n\n}\n\nfunc (a *AlertFilter) pipeline(item v1.Alert, out chan v1.Alert) {\n\tif len(a.Labels) > 0 {\n\t\tif !a.matchLabel(item) {\n\t\t\treturn\n\t\t}\n\t}\n\tif !a.ActiveAt.IsZero() {\n\t\tif item.ActiveAt.Before(a.ActiveAt) {\n\t\t\treturn\n\t\t}\n\t}\n\tif len(a.States) > 0 {\n\t\tif !a.states[string(item.State)] {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif a.Value != nil {\n\t\tif !a.Value.MatchString(item.Value) {\n\t\t\treturn\n\t\t}\n\t}\n\tout <- item\n}\n\nfunc (a *AlertFilter) matchLabel(item v1.Alert) bool {\n\tfor k, v := range item.Labels {\n\t\tmatched, ok := a.Labels[string(k)]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Exact match or we aren't matching on values.\n\t\tswitch string(v) {\n\t\tcase \"\", matched:\n\t\t\treturn true\n\t\t}\n\n\t\t// Let's see if this was a regex match.\n\t\tr := a.labelRegexes[string(k)]\n\t\t// It wasn't, so return false because we didn't have an exact match.\n\t\tif r == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif r.MatchString(string(v)) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Alerts will return all the alerts that match the filter.\nfunc (c *Client) Alerts(ctx context.Context, filter AlertFilter) (chan v1.Alert, error) {\n\tr, err := c.client.Alerts(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn filter.filter(r.Alerts)\n}\n"
  },
  {
    "path": "chapter/11/ops/internal/server/server.go",
    "content": "// Package server contains our gRPC server implementation for the ops server.\npackage server\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"net\"\n\t\"sort\"\n\t\"sync\"\n\t\"time\"\n\n\tjaeger \"github.com/PacktPublishing/Go-for-DevOps/chapter/11/ops/internal/jaeger/client\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/ops/internal/prom\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/client\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/reflection\"\n\t\"google.golang.org/protobuf/types/known/durationpb\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/11/ops/proto\"\n\tmpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/11/ops/proto/jaeger/model\"\n)\n\n// API implements our gRPC server's API.\ntype API struct {\n\tpb.UnimplementedOpsServer\n\n\taddr string\n\n\tgrpcServer *grpc.Server\n\tgOpts      []grpc.ServerOption\n\tmu         sync.Mutex\n\n\tclients Clients\n}\n\n// Clients holds the remote clients requires to do ops.\ntype Clients struct {\n\t// Jaeger provides access to traces.\n\tJaeger *jaeger.Jaeger\n\t// Prom provides access to metrics.\n\tProm *prom.Client\n\t// Petstore provides access to the petstore.\n\tPetstore *client.Client\n}\n\nfunc (c Clients) validate() error {\n\tif c.Jaeger == nil {\n\t\treturn errors.New(\"Jaeger cannot be nil\")\n\t}\n\tif c.Prom == nil {\n\t\treturn errors.New(\"Prom cannot be nil\")\n\t}\n\tif c.Petstore == nil {\n\t\treturn errors.New(\"PetStore cannot be nil\")\n\t}\n\treturn nil\n}\n\n// Option is an optional arguments to New().\ntype Option func(a *API)\n\n// WithGRPCOpts creates the gRPC server with the options passed.\nfunc WithGRPCOpts(opts ...grpc.ServerOption) Option {\n\treturn func(a *API) {\n\t\ta.gOpts = append(a.gOpts, opts...)\n\t}\n}\n\n// New is the constructore for API.\nfunc New(addr string, clients Clients, options ...Option) (*API, error) {\n\tif err := clients.validate(); err != nil {\n\t\treturn nil, err\n\t}\n\n\ta := &API{addr: addr, clients: clients}\n\n\tfor _, o := range options {\n\t\to(a)\n\t}\n\n\ta.grpcServer = grpc.NewServer(a.gOpts...)\n\ta.grpcServer.RegisterService(&pb.Ops_ServiceDesc, a)\n\treflection.Register(a.grpcServer)\n\n\treturn a, nil\n}\n\n// Start starts the server. This blocks until Stop() is called.\nfunc (a *API) Start() error {\n\ta.mu.Lock()\n\tdefer a.mu.Unlock()\n\n\tlis, err := net.Listen(\"tcp\", a.addr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn a.grpcServer.Serve(lis)\n}\n\n// Stop stops the server.\nfunc (a *API) Stop() {\n\ta.mu.Lock()\n\tdefer a.mu.Unlock()\n\n\ta.grpcServer.Stop()\n}\n\n// ListTraces lists recent traces from Jaeger for the petstore service.\nfunc (a *API) ListTraces(ctx context.Context, req *pb.ListTracesReq) (*pb.ListTracesResp, error) {\n\tlog.Println(\"tags:\", req.Tags)\n\tparams := jaeger.SearchParams{\n\t\tService:     \"petstore\",\n\t\tOperation:   req.Operation,\n\t\tTags:        req.Tags,\n\t\tDurationMin: time.Duration(req.DurationMin),\n\t\tDurationMax: time.Duration(req.DurationMax),\n\t\tSearchDepth: req.SearchDepth,\n\t}\n\n\tif req.Start > 0 {\n\t\tparams.Start = time.Unix(0, req.Start)\n\t}\n\tif req.End > 0 {\n\t\tparams.End = time.Unix(0, req.End)\n\t}\n\n\tch, err := a.clients.Jaeger.Search(ctx, params)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tresp := &pb.ListTracesResp{}\n\tfor trace := range ch {\n\t\tif len(trace.Spans) < 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif trace.Err != nil {\n\t\t\treturn nil, trace.Err\n\t\t}\n\n\t\tstart := trace.Spans[0].Span.StartTime\n\t\tt := time.Unix(int64(start.Seconds), int64(start.Nanos)).UTC()\n\t\tresp.Traces = append(resp.Traces, &pb.TraceItem{Start: t.UnixNano(), Id: trace.ID})\n\t}\n\tsort.Slice(\n\t\tresp.Traces,\n\t\tfunc(i, j int) bool {\n\t\t\tif resp.Traces[i].Start > resp.Traces[j].Start {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\t)\n\treturn resp, nil\n}\n\nfunc (a *API) ShowLogs(ctx context.Context, req *pb.ShowLogsReq) (*pb.ShowLogsResp, error) {\n\tt, err := a.clients.Jaeger.Trace(ctx, req.Id)\n\tif err != nil {\n\t\tif err == jaeger.ErrNotFound {\n\t\t\treturn &pb.ShowLogsResp{}, nil\n\t\t}\n\t\treturn nil, err\n\t}\n\n\tlogs := []*mpb.Log{}\n\tfor _, span := range t.Spans {\n\t\tlogs = append(logs, span.Logs...)\n\t}\n\n\t/*\n\t\tsort.SliceStable(\n\t\t\tlogs,\n\t\t\tfunc(i, j int) bool {\n\t\t\t\tif logs[i].Timestamp.AsTime().Before(logs[j].Timestamp.AsTime()) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\treturn false\n\t\t\t},\n\t\t)\n\t*/\n\n\treturn &pb.ShowLogsResp{\n\t\tId:   t.ID,\n\t\tLogs: logs,\n\t}, nil\n}\n\nfunc (a *API) ShowTrace(ctx context.Context, req *pb.ShowTraceReq) (*pb.ShowTraceResp, error) {\n\tt, err := a.clients.Jaeger.Trace(ctx, req.Id)\n\tif err != nil {\n\t\tif err == jaeger.ErrNotFound {\n\t\t\treturn &pb.ShowTraceResp{}, nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tvar (\n\t\tops    []string\n\t\terrors []string\n\t\ttags   []string\n\t\tdur    *durationpb.Duration\n\t)\n\tfor _, span := range t.Spans {\n\t\tops = append(ops, span.OperationName)\n\t\tfor _, kv := range span.Tags {\n\t\t\tif kv.Key == \"error\" {\n\t\t\t\terrors = append(errors, kv.VStr)\n\t\t\t}\n\t\t\ttags = append(tags, kv.Key)\n\t\t}\n\t\tif span.Duration.AsDuration() > dur.AsDuration() {\n\t\t\tdur = span.Duration\n\t\t}\n\t}\n\n\treturn &pb.ShowTraceResp{\n\t\tId:         t.ID,\n\t\tOperations: ops,\n\t\tErrors:     errors,\n\t\tTags:       tags,\n\t\tDuration:   dur,\n\t}, nil\n}\n\n// ChangeSampling changes the sampling type and rate for the Petstore.\nfunc (a *API) ChangeSampling(ctx context.Context, req *pb.ChangeSamplingReq) (*pb.ChangeSamplingResp, error) {\n\tsc := client.Sampler{\n\t\tType: client.SamplerType(req.Type),\n\t\tRate: req.FloatValue,\n\t}\n\n\tif err := a.clients.Petstore.ChangeSampler(ctx, sc); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &pb.ChangeSamplingResp{}, nil\n}\n\n// DeployedVersion returns the version of the Petstore that prometheus says is current.\nfunc (a *API) DeployedVersion(ctx context.Context, req *pb.DeployedVersionReq) (*pb.DeployedVersionResp, error) {\n\tmv, _, err := a.clients.Prom.Metric(ctx, \"deployedVersion\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem getting metric: %w\", err)\n\t}\n\treturn &pb.DeployedVersionResp{Version: mv.String()}, nil\n}\n\n// Alerts grabs all currnetly firing alerts.\nfunc (a *API) Alerts(ctx context.Context, req *pb.AlertsReq) (*pb.AlertsResp, error) {\n\tlabels := map[string]string{}\n\tfor _, l := range req.Labels {\n\t\tlabels[l] = \"\"\n\t}\n\tfilter := prom.AlertFilter{\n\t\tLabels:   labels,\n\t\tActiveAt: time.Unix(0, req.ActiveAt),\n\t\tStates:   req.States,\n\t}\n\n\tch, err := a.clients.Prom.Alerts(ctx, filter)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tresp := &pb.AlertsResp{}\n\tfor a := range ch {\n\t\tresp.Alerts = append(\n\t\t\tresp.Alerts,\n\t\t\t&pb.Alert{\n\t\t\t\tState:    string(a.State),\n\t\t\t\tValue:    a.Value,\n\t\t\t\tActiveAt: a.ActiveAt.UnixNano(),\n\t\t\t},\n\t\t)\n\t}\n\treturn resp, nil\n}\n"
  },
  {
    "path": "chapter/11/ops/ops.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"log\"\n\n\tjaegerClient \"github.com/PacktPublishing/Go-for-DevOps/chapter/11/ops/internal/jaeger/client\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/ops/internal/prom\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/ops/internal/server\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/client\"\n)\n\nvar (\n\taddr         = flag.String(\"addr\", \"0.0.0.0:7000\", \"The address to run the service on.\")\n\tjaegerAddr   = flag.String(\"jaegerAddr\", \"127.0.0.1:16685\", \"The address of the Jaeger query service.\")\n\tpromAddr     = flag.String(\"promAddr\", \"127.0.0.1:9000\", \"The address of the Prometheus service.\")\n\tpetstoreAddr = flag.String(\"petstoreAddr\", \"127.0.0.1:6742\", \"The address of the Petstore.\")\n)\n\nfunc main() {\n\tflag.Parse()\n\n\tj, err := jaegerClient.New(*jaegerAddr)\n\tif err != nil {\n\t\tlog.Fatalf(\"could not connect to Jaeger: %s\", err)\n\t}\n\n\tp, err := prom.New(\"http://\" + *promAddr)\n\tif err != nil {\n\t\tlog.Fatalf(\"could not connect to Prometheus: %s\", err)\n\t}\n\n\tps, err := client.New(*petstoreAddr)\n\tif err != nil {\n\t\tlog.Fatalf(\"could not connecto the Petstore: %s\", err)\n\t}\n\tclients := server.Clients{\n\t\tJaeger:   j,\n\t\tProm:     p,\n\t\tPetstore: ps,\n\t}\n\tserv, err := server.New(*addr, clients)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tlog.Println(\"serving starting on: \", *addr)\n\tserv.Start()\n}\n"
  },
  {
    "path": "chapter/11/ops/proto/buf.gen.yaml",
    "content": "version: v1\nplugins:\n  - name: go\n    out: ./\n    opt:\n      - paths=source_relative\n  - name: go-grpc\n    out: ./\n    opt:\n      - paths=source_relative\n"
  },
  {
    "path": "chapter/11/ops/proto/buf.yaml",
    "content": "version: v1\ndeps:\n  - buf.build/googleapis/googleapis\n  - buf.build/gogo/protobuf\nlint:\n  use:\n    - DEFAULT\nbreaking:\n  use:\n    - FILE\n"
  },
  {
    "path": "chapter/11/ops/proto/jaeger/collector.pb.go",
    "content": "// Copyright (c) 2019 The Jaeger Authors.\n// Copyright (c) 2018 Uber Technologies, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.25.0-devel\n// \tprotoc        v3.18.0\n// source: jaeger/collector.proto\n\npackage jaeger\n\nimport (\n\tmodel \"github.com/PacktPublishing/Go-for-DevOps/chapter/11/ops/proto/jaeger/model\"\n\t_ \"github.com/gogo/protobuf/gogoproto\"\n\t_ \"google.golang.org/genproto/googleapis/api/annotations\"\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 PostSpansRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tBatch *model.Batch `protobuf:\"bytes,1,opt,name=batch,proto3\" json:\"batch,omitempty\"`\n}\n\nfunc (x *PostSpansRequest) Reset() {\n\t*x = PostSpansRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_collector_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *PostSpansRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*PostSpansRequest) ProtoMessage() {}\n\nfunc (x *PostSpansRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_collector_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 PostSpansRequest.ProtoReflect.Descriptor instead.\nfunc (*PostSpansRequest) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_collector_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *PostSpansRequest) GetBatch() *model.Batch {\n\tif x != nil {\n\t\treturn x.Batch\n\t}\n\treturn nil\n}\n\ntype PostSpansResponse struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *PostSpansResponse) Reset() {\n\t*x = PostSpansResponse{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_collector_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *PostSpansResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*PostSpansResponse) ProtoMessage() {}\n\nfunc (x *PostSpansResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_collector_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 PostSpansResponse.ProtoReflect.Descriptor instead.\nfunc (*PostSpansResponse) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_collector_proto_rawDescGZIP(), []int{1}\n}\n\nvar File_jaeger_collector_proto protoreflect.FileDescriptor\n\nvar file_jaeger_collector_proto_rawDesc = []byte{\n\t0x0a, 0x16, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74,\n\t0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72,\n\t0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x1a, 0x18, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2f,\n\t0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x1a, 0x14, 0x67, 0x6f, 0x67, 0x6f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x67,\n\t0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,\n\t0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x44, 0x0a, 0x10, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x70, 0x61,\n\t0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x05, 0x62, 0x61, 0x74,\n\t0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65,\n\t0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x42, 0x04,\n\t0xc8, 0xde, 0x1f, 0x00, 0x52, 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x22, 0x13, 0x0a, 0x11, 0x50,\n\t0x6f, 0x73, 0x74, 0x53, 0x70, 0x61, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,\n\t0x32, 0x7c, 0x0a, 0x10, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x65, 0x72,\n\t0x76, 0x69, 0x63, 0x65, 0x12, 0x68, 0x0a, 0x09, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x70, 0x61, 0x6e,\n\t0x73, 0x12, 0x1f, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76,\n\t0x32, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x70, 0x61, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,\n\t0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f,\n\t0x76, 0x32, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x70, 0x61, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70,\n\t0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x61,\n\t0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x73, 0x70, 0x61, 0x6e, 0x73, 0x3a, 0x01, 0x2a, 0x42, 0x6b,\n\t0x0a, 0x17, 0x69, 0x6f, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x74, 0x72, 0x61, 0x63, 0x69,\n\t0x6e, 0x67, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75,\n\t0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x61, 0x63, 0x6b, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69,\n\t0x73, 0x68, 0x69, 0x6e, 0x67, 0x2f, 0x47, 0x6f, 0x2d, 0x66, 0x6f, 0x72, 0x2d, 0x44, 0x65, 0x76,\n\t0x4f, 0x70, 0x73, 0x2f, 0x63, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x2f, 0x31, 0x30, 0x2f, 0x6f,\n\t0x70, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0xc8,\n\t0xe2, 0x1e, 0x01, 0xd0, 0xe2, 0x1e, 0x01, 0xe0, 0xe2, 0x1e, 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_jaeger_collector_proto_rawDescOnce sync.Once\n\tfile_jaeger_collector_proto_rawDescData = file_jaeger_collector_proto_rawDesc\n)\n\nfunc file_jaeger_collector_proto_rawDescGZIP() []byte {\n\tfile_jaeger_collector_proto_rawDescOnce.Do(func() {\n\t\tfile_jaeger_collector_proto_rawDescData = protoimpl.X.CompressGZIP(file_jaeger_collector_proto_rawDescData)\n\t})\n\treturn file_jaeger_collector_proto_rawDescData\n}\n\nvar file_jaeger_collector_proto_msgTypes = make([]protoimpl.MessageInfo, 2)\nvar file_jaeger_collector_proto_goTypes = []interface{}{\n\t(*PostSpansRequest)(nil),  // 0: jaeger.api_v2.PostSpansRequest\n\t(*PostSpansResponse)(nil), // 1: jaeger.api_v2.PostSpansResponse\n\t(*model.Batch)(nil),       // 2: jaeger.api_v2.Batch\n}\nvar file_jaeger_collector_proto_depIdxs = []int32{\n\t2, // 0: jaeger.api_v2.PostSpansRequest.batch:type_name -> jaeger.api_v2.Batch\n\t0, // 1: jaeger.api_v2.CollectorService.PostSpans:input_type -> jaeger.api_v2.PostSpansRequest\n\t1, // 2: jaeger.api_v2.CollectorService.PostSpans:output_type -> jaeger.api_v2.PostSpansResponse\n\t2, // [2:3] is the sub-list for method output_type\n\t1, // [1:2] is the sub-list for method input_type\n\t1, // [1:1] is the sub-list for extension type_name\n\t1, // [1:1] is the sub-list for extension extendee\n\t0, // [0:1] is the sub-list for field type_name\n}\n\nfunc init() { file_jaeger_collector_proto_init() }\nfunc file_jaeger_collector_proto_init() {\n\tif File_jaeger_collector_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_jaeger_collector_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*PostSpansRequest); 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_jaeger_collector_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*PostSpansResponse); 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_jaeger_collector_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_jaeger_collector_proto_goTypes,\n\t\tDependencyIndexes: file_jaeger_collector_proto_depIdxs,\n\t\tMessageInfos:      file_jaeger_collector_proto_msgTypes,\n\t}.Build()\n\tFile_jaeger_collector_proto = out.File\n\tfile_jaeger_collector_proto_rawDesc = nil\n\tfile_jaeger_collector_proto_goTypes = nil\n\tfile_jaeger_collector_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "chapter/11/ops/proto/jaeger/collector.proto",
    "content": "// Copyright (c) 2019 The Jaeger Authors.\n// Copyright (c) 2018 Uber Technologies, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax=\"proto3\";\n\npackage jaeger.api_v2;\n\nimport \"jaeger/model/model.proto\";\nimport \"gogoproto/gogo.proto\";\nimport \"google/api/annotations.proto\";\n\noption go_package = \"github.com/PacktPublishing/Go-for-DevOps/chapter/10/ops/proto/jaeger\";\noption java_package = \"io.jaegertracing.api_v2\";\n\n// Enable gogoprotobuf extensions (https://github.com/gogo/protobuf/blob/master/extensions.md).\n// Enable custom Marshal method.\noption (gogoproto.marshaler_all) = true;\n// Enable custom Unmarshal method.\noption (gogoproto.unmarshaler_all) = true;\n// Enable custom Size method (Required by Marshal and Unmarshal).\noption (gogoproto.sizer_all) = true;\n\n\nmessage PostSpansRequest {\n    Batch batch = 1 [\n        (gogoproto.nullable) = false\n    ];\n}\n\nmessage PostSpansResponse {\n}\n\nservice CollectorService {\n    rpc PostSpans(PostSpansRequest) returns (PostSpansResponse) {\n        option (google.api.http) = {\n            post: \"/api/v2/spans\"\n            body: \"*\"\n        };\n    }\n}\n"
  },
  {
    "path": "chapter/11/ops/proto/jaeger/collector_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n\npackage jaeger\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// CollectorServiceClient is the client API for CollectorService 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 CollectorServiceClient interface {\n\tPostSpans(ctx context.Context, in *PostSpansRequest, opts ...grpc.CallOption) (*PostSpansResponse, error)\n}\n\ntype collectorServiceClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewCollectorServiceClient(cc grpc.ClientConnInterface) CollectorServiceClient {\n\treturn &collectorServiceClient{cc}\n}\n\nfunc (c *collectorServiceClient) PostSpans(ctx context.Context, in *PostSpansRequest, opts ...grpc.CallOption) (*PostSpansResponse, error) {\n\tout := new(PostSpansResponse)\n\terr := c.cc.Invoke(ctx, \"/jaeger.api_v2.CollectorService/PostSpans\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// CollectorServiceServer is the server API for CollectorService service.\n// All implementations must embed UnimplementedCollectorServiceServer\n// for forward compatibility\ntype CollectorServiceServer interface {\n\tPostSpans(context.Context, *PostSpansRequest) (*PostSpansResponse, error)\n\tmustEmbedUnimplementedCollectorServiceServer()\n}\n\n// UnimplementedCollectorServiceServer must be embedded to have forward compatible implementations.\ntype UnimplementedCollectorServiceServer struct {\n}\n\nfunc (UnimplementedCollectorServiceServer) PostSpans(context.Context, *PostSpansRequest) (*PostSpansResponse, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method PostSpans not implemented\")\n}\nfunc (UnimplementedCollectorServiceServer) mustEmbedUnimplementedCollectorServiceServer() {}\n\n// UnsafeCollectorServiceServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to CollectorServiceServer will\n// result in compilation errors.\ntype UnsafeCollectorServiceServer interface {\n\tmustEmbedUnimplementedCollectorServiceServer()\n}\n\nfunc RegisterCollectorServiceServer(s grpc.ServiceRegistrar, srv CollectorServiceServer) {\n\ts.RegisterService(&CollectorService_ServiceDesc, srv)\n}\n\nfunc _CollectorService_PostSpans_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(PostSpansRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(CollectorServiceServer).PostSpans(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/jaeger.api_v2.CollectorService/PostSpans\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(CollectorServiceServer).PostSpans(ctx, req.(*PostSpansRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// CollectorService_ServiceDesc is the grpc.ServiceDesc for CollectorService 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 CollectorService_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"jaeger.api_v2.CollectorService\",\n\tHandlerType: (*CollectorServiceServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"PostSpans\",\n\t\t\tHandler:    _CollectorService_PostSpans_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"jaeger/collector.proto\",\n}\n"
  },
  {
    "path": "chapter/11/ops/proto/jaeger/model/model.pb.go",
    "content": "// Copyright (c) 2018 Uber Technologies, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.25.0-devel\n// \tprotoc        v3.18.0\n// source: jaeger/model/model.proto\n\npackage model\n\nimport (\n\t_ \"github.com/gogo/protobuf/gogoproto\"\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\tdurationpb \"google.golang.org/protobuf/types/known/durationpb\"\n\ttimestamppb \"google.golang.org/protobuf/types/known/timestamppb\"\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 ValueType int32\n\nconst (\n\tValueType_STRING  ValueType = 0\n\tValueType_BOOL    ValueType = 1\n\tValueType_INT64   ValueType = 2\n\tValueType_FLOAT64 ValueType = 3\n\tValueType_BINARY  ValueType = 4\n)\n\n// Enum value maps for ValueType.\nvar (\n\tValueType_name = map[int32]string{\n\t\t0: \"STRING\",\n\t\t1: \"BOOL\",\n\t\t2: \"INT64\",\n\t\t3: \"FLOAT64\",\n\t\t4: \"BINARY\",\n\t}\n\tValueType_value = map[string]int32{\n\t\t\"STRING\":  0,\n\t\t\"BOOL\":    1,\n\t\t\"INT64\":   2,\n\t\t\"FLOAT64\": 3,\n\t\t\"BINARY\":  4,\n\t}\n)\n\nfunc (x ValueType) Enum() *ValueType {\n\tp := new(ValueType)\n\t*p = x\n\treturn p\n}\n\nfunc (x ValueType) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (ValueType) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_jaeger_model_model_proto_enumTypes[0].Descriptor()\n}\n\nfunc (ValueType) Type() protoreflect.EnumType {\n\treturn &file_jaeger_model_model_proto_enumTypes[0]\n}\n\nfunc (x ValueType) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use ValueType.Descriptor instead.\nfunc (ValueType) EnumDescriptor() ([]byte, []int) {\n\treturn file_jaeger_model_model_proto_rawDescGZIP(), []int{0}\n}\n\ntype SpanRefType int32\n\nconst (\n\tSpanRefType_CHILD_OF     SpanRefType = 0\n\tSpanRefType_FOLLOWS_FROM SpanRefType = 1\n)\n\n// Enum value maps for SpanRefType.\nvar (\n\tSpanRefType_name = map[int32]string{\n\t\t0: \"CHILD_OF\",\n\t\t1: \"FOLLOWS_FROM\",\n\t}\n\tSpanRefType_value = map[string]int32{\n\t\t\"CHILD_OF\":     0,\n\t\t\"FOLLOWS_FROM\": 1,\n\t}\n)\n\nfunc (x SpanRefType) Enum() *SpanRefType {\n\tp := new(SpanRefType)\n\t*p = x\n\treturn p\n}\n\nfunc (x SpanRefType) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (SpanRefType) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_jaeger_model_model_proto_enumTypes[1].Descriptor()\n}\n\nfunc (SpanRefType) Type() protoreflect.EnumType {\n\treturn &file_jaeger_model_model_proto_enumTypes[1]\n}\n\nfunc (x SpanRefType) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use SpanRefType.Descriptor instead.\nfunc (SpanRefType) EnumDescriptor() ([]byte, []int) {\n\treturn file_jaeger_model_model_proto_rawDescGZIP(), []int{1}\n}\n\ntype KeyValue struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tKey      string    `protobuf:\"bytes,1,opt,name=key,proto3\" json:\"key,omitempty\"`\n\tVType    ValueType `protobuf:\"varint,2,opt,name=v_type,json=vType,proto3,enum=jaeger.api_v2.ValueType\" json:\"v_type,omitempty\"`\n\tVStr     string    `protobuf:\"bytes,3,opt,name=v_str,json=vStr,proto3\" json:\"v_str,omitempty\"`\n\tVBool    bool      `protobuf:\"varint,4,opt,name=v_bool,json=vBool,proto3\" json:\"v_bool,omitempty\"`\n\tVInt64   int64     `protobuf:\"varint,5,opt,name=v_int64,json=vInt64,proto3\" json:\"v_int64,omitempty\"`\n\tVFloat64 float64   `protobuf:\"fixed64,6,opt,name=v_float64,json=vFloat64,proto3\" json:\"v_float64,omitempty\"`\n\tVBinary  []byte    `protobuf:\"bytes,7,opt,name=v_binary,json=vBinary,proto3\" json:\"v_binary,omitempty\"`\n}\n\nfunc (x *KeyValue) Reset() {\n\t*x = KeyValue{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_model_model_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *KeyValue) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*KeyValue) ProtoMessage() {}\n\nfunc (x *KeyValue) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_model_model_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 KeyValue.ProtoReflect.Descriptor instead.\nfunc (*KeyValue) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_model_model_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *KeyValue) GetKey() string {\n\tif x != nil {\n\t\treturn x.Key\n\t}\n\treturn \"\"\n}\n\nfunc (x *KeyValue) GetVType() ValueType {\n\tif x != nil {\n\t\treturn x.VType\n\t}\n\treturn ValueType_STRING\n}\n\nfunc (x *KeyValue) GetVStr() string {\n\tif x != nil {\n\t\treturn x.VStr\n\t}\n\treturn \"\"\n}\n\nfunc (x *KeyValue) GetVBool() bool {\n\tif x != nil {\n\t\treturn x.VBool\n\t}\n\treturn false\n}\n\nfunc (x *KeyValue) GetVInt64() int64 {\n\tif x != nil {\n\t\treturn x.VInt64\n\t}\n\treturn 0\n}\n\nfunc (x *KeyValue) GetVFloat64() float64 {\n\tif x != nil {\n\t\treturn x.VFloat64\n\t}\n\treturn 0\n}\n\nfunc (x *KeyValue) GetVBinary() []byte {\n\tif x != nil {\n\t\treturn x.VBinary\n\t}\n\treturn nil\n}\n\ntype Log struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tTimestamp *timestamppb.Timestamp `protobuf:\"bytes,1,opt,name=timestamp,proto3\" json:\"timestamp,omitempty\"`\n\tFields    []*KeyValue            `protobuf:\"bytes,2,rep,name=fields,proto3\" json:\"fields,omitempty\"`\n}\n\nfunc (x *Log) Reset() {\n\t*x = Log{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_model_model_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Log) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Log) ProtoMessage() {}\n\nfunc (x *Log) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_model_model_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 Log.ProtoReflect.Descriptor instead.\nfunc (*Log) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_model_model_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *Log) GetTimestamp() *timestamppb.Timestamp {\n\tif x != nil {\n\t\treturn x.Timestamp\n\t}\n\treturn nil\n}\n\nfunc (x *Log) GetFields() []*KeyValue {\n\tif x != nil {\n\t\treturn x.Fields\n\t}\n\treturn nil\n}\n\ntype SpanRef struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tTraceId []byte      `protobuf:\"bytes,1,opt,name=trace_id,json=traceId,proto3\" json:\"trace_id,omitempty\"`\n\tSpanId  []byte      `protobuf:\"bytes,2,opt,name=span_id,json=spanId,proto3\" json:\"span_id,omitempty\"`\n\tRefType SpanRefType `protobuf:\"varint,3,opt,name=ref_type,json=refType,proto3,enum=jaeger.api_v2.SpanRefType\" json:\"ref_type,omitempty\"`\n}\n\nfunc (x *SpanRef) Reset() {\n\t*x = SpanRef{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_model_model_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *SpanRef) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*SpanRef) ProtoMessage() {}\n\nfunc (x *SpanRef) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_model_model_proto_msgTypes[2]\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 SpanRef.ProtoReflect.Descriptor instead.\nfunc (*SpanRef) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_model_model_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *SpanRef) GetTraceId() []byte {\n\tif x != nil {\n\t\treturn x.TraceId\n\t}\n\treturn nil\n}\n\nfunc (x *SpanRef) GetSpanId() []byte {\n\tif x != nil {\n\t\treturn x.SpanId\n\t}\n\treturn nil\n}\n\nfunc (x *SpanRef) GetRefType() SpanRefType {\n\tif x != nil {\n\t\treturn x.RefType\n\t}\n\treturn SpanRefType_CHILD_OF\n}\n\ntype Process struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tServiceName string      `protobuf:\"bytes,1,opt,name=service_name,json=serviceName,proto3\" json:\"service_name,omitempty\"`\n\tTags        []*KeyValue `protobuf:\"bytes,2,rep,name=tags,proto3\" json:\"tags,omitempty\"`\n}\n\nfunc (x *Process) Reset() {\n\t*x = Process{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_model_model_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Process) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Process) ProtoMessage() {}\n\nfunc (x *Process) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_model_model_proto_msgTypes[3]\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 Process.ProtoReflect.Descriptor instead.\nfunc (*Process) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_model_model_proto_rawDescGZIP(), []int{3}\n}\n\nfunc (x *Process) GetServiceName() string {\n\tif x != nil {\n\t\treturn x.ServiceName\n\t}\n\treturn \"\"\n}\n\nfunc (x *Process) GetTags() []*KeyValue {\n\tif x != nil {\n\t\treturn x.Tags\n\t}\n\treturn nil\n}\n\ntype Span struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tTraceId       []byte                 `protobuf:\"bytes,1,opt,name=trace_id,json=traceId,proto3\" json:\"trace_id,omitempty\"`\n\tSpanId        []byte                 `protobuf:\"bytes,2,opt,name=span_id,json=spanId,proto3\" json:\"span_id,omitempty\"`\n\tOperationName string                 `protobuf:\"bytes,3,opt,name=operation_name,json=operationName,proto3\" json:\"operation_name,omitempty\"`\n\tReferences    []*SpanRef             `protobuf:\"bytes,4,rep,name=references,proto3\" json:\"references,omitempty\"`\n\tFlags         uint32                 `protobuf:\"varint,5,opt,name=flags,proto3\" json:\"flags,omitempty\"`\n\tStartTime     *timestamppb.Timestamp `protobuf:\"bytes,6,opt,name=start_time,json=startTime,proto3\" json:\"start_time,omitempty\"`\n\tDuration      *durationpb.Duration   `protobuf:\"bytes,7,opt,name=duration,proto3\" json:\"duration,omitempty\"`\n\tTags          []*KeyValue            `protobuf:\"bytes,8,rep,name=tags,proto3\" json:\"tags,omitempty\"`\n\tLogs          []*Log                 `protobuf:\"bytes,9,rep,name=logs,proto3\" json:\"logs,omitempty\"`\n\tProcess       *Process               `protobuf:\"bytes,10,opt,name=process,proto3\" json:\"process,omitempty\"`\n\tProcessId     string                 `protobuf:\"bytes,11,opt,name=process_id,json=processId,proto3\" json:\"process_id,omitempty\"`\n\tWarnings      []string               `protobuf:\"bytes,12,rep,name=warnings,proto3\" json:\"warnings,omitempty\"`\n}\n\nfunc (x *Span) Reset() {\n\t*x = Span{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_model_model_proto_msgTypes[4]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Span) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Span) ProtoMessage() {}\n\nfunc (x *Span) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_model_model_proto_msgTypes[4]\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 Span.ProtoReflect.Descriptor instead.\nfunc (*Span) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_model_model_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (x *Span) GetTraceId() []byte {\n\tif x != nil {\n\t\treturn x.TraceId\n\t}\n\treturn nil\n}\n\nfunc (x *Span) GetSpanId() []byte {\n\tif x != nil {\n\t\treturn x.SpanId\n\t}\n\treturn nil\n}\n\nfunc (x *Span) GetOperationName() string {\n\tif x != nil {\n\t\treturn x.OperationName\n\t}\n\treturn \"\"\n}\n\nfunc (x *Span) GetReferences() []*SpanRef {\n\tif x != nil {\n\t\treturn x.References\n\t}\n\treturn nil\n}\n\nfunc (x *Span) GetFlags() uint32 {\n\tif x != nil {\n\t\treturn x.Flags\n\t}\n\treturn 0\n}\n\nfunc (x *Span) GetStartTime() *timestamppb.Timestamp {\n\tif x != nil {\n\t\treturn x.StartTime\n\t}\n\treturn nil\n}\n\nfunc (x *Span) GetDuration() *durationpb.Duration {\n\tif x != nil {\n\t\treturn x.Duration\n\t}\n\treturn nil\n}\n\nfunc (x *Span) GetTags() []*KeyValue {\n\tif x != nil {\n\t\treturn x.Tags\n\t}\n\treturn nil\n}\n\nfunc (x *Span) GetLogs() []*Log {\n\tif x != nil {\n\t\treturn x.Logs\n\t}\n\treturn nil\n}\n\nfunc (x *Span) GetProcess() *Process {\n\tif x != nil {\n\t\treturn x.Process\n\t}\n\treturn nil\n}\n\nfunc (x *Span) GetProcessId() string {\n\tif x != nil {\n\t\treturn x.ProcessId\n\t}\n\treturn \"\"\n}\n\nfunc (x *Span) GetWarnings() []string {\n\tif x != nil {\n\t\treturn x.Warnings\n\t}\n\treturn nil\n}\n\ntype Trace struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tSpans      []*Span                 `protobuf:\"bytes,1,rep,name=spans,proto3\" json:\"spans,omitempty\"`\n\tProcessMap []*Trace_ProcessMapping `protobuf:\"bytes,2,rep,name=process_map,json=processMap,proto3\" json:\"process_map,omitempty\"`\n\tWarnings   []string                `protobuf:\"bytes,3,rep,name=warnings,proto3\" json:\"warnings,omitempty\"`\n}\n\nfunc (x *Trace) Reset() {\n\t*x = Trace{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_model_model_proto_msgTypes[5]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Trace) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Trace) ProtoMessage() {}\n\nfunc (x *Trace) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_model_model_proto_msgTypes[5]\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 Trace.ProtoReflect.Descriptor instead.\nfunc (*Trace) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_model_model_proto_rawDescGZIP(), []int{5}\n}\n\nfunc (x *Trace) GetSpans() []*Span {\n\tif x != nil {\n\t\treturn x.Spans\n\t}\n\treturn nil\n}\n\nfunc (x *Trace) GetProcessMap() []*Trace_ProcessMapping {\n\tif x != nil {\n\t\treturn x.ProcessMap\n\t}\n\treturn nil\n}\n\nfunc (x *Trace) GetWarnings() []string {\n\tif x != nil {\n\t\treturn x.Warnings\n\t}\n\treturn nil\n}\n\n// Note that both Span and Batch may contain a Process.\n// This is different from the Thrift model which was only used\n// for transport, because Proto model is also used by the backend\n// as the domain model, where once a batch is received it is split\n// into individual spans which are all processed independently,\n// and therefore they all need a Process. As far as on-the-wire\n// semantics, both Batch and Spans in the same message may contain\n// their own instances of Process, with span.Process taking priority\n// over batch.Process.\ntype Batch struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tSpans   []*Span  `protobuf:\"bytes,1,rep,name=spans,proto3\" json:\"spans,omitempty\"`\n\tProcess *Process `protobuf:\"bytes,2,opt,name=process,proto3\" json:\"process,omitempty\"`\n}\n\nfunc (x *Batch) Reset() {\n\t*x = Batch{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_model_model_proto_msgTypes[6]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Batch) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Batch) ProtoMessage() {}\n\nfunc (x *Batch) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_model_model_proto_msgTypes[6]\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 Batch.ProtoReflect.Descriptor instead.\nfunc (*Batch) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_model_model_proto_rawDescGZIP(), []int{6}\n}\n\nfunc (x *Batch) GetSpans() []*Span {\n\tif x != nil {\n\t\treturn x.Spans\n\t}\n\treturn nil\n}\n\nfunc (x *Batch) GetProcess() *Process {\n\tif x != nil {\n\t\treturn x.Process\n\t}\n\treturn nil\n}\n\ntype DependencyLink struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tParent    string `protobuf:\"bytes,1,opt,name=parent,proto3\" json:\"parent,omitempty\"`\n\tChild     string `protobuf:\"bytes,2,opt,name=child,proto3\" json:\"child,omitempty\"`\n\tCallCount uint64 `protobuf:\"varint,3,opt,name=call_count,json=callCount,proto3\" json:\"call_count,omitempty\"`\n\tSource    string `protobuf:\"bytes,4,opt,name=source,proto3\" json:\"source,omitempty\"`\n}\n\nfunc (x *DependencyLink) Reset() {\n\t*x = DependencyLink{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_model_model_proto_msgTypes[7]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DependencyLink) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DependencyLink) ProtoMessage() {}\n\nfunc (x *DependencyLink) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_model_model_proto_msgTypes[7]\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 DependencyLink.ProtoReflect.Descriptor instead.\nfunc (*DependencyLink) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_model_model_proto_rawDescGZIP(), []int{7}\n}\n\nfunc (x *DependencyLink) GetParent() string {\n\tif x != nil {\n\t\treturn x.Parent\n\t}\n\treturn \"\"\n}\n\nfunc (x *DependencyLink) GetChild() string {\n\tif x != nil {\n\t\treturn x.Child\n\t}\n\treturn \"\"\n}\n\nfunc (x *DependencyLink) GetCallCount() uint64 {\n\tif x != nil {\n\t\treturn x.CallCount\n\t}\n\treturn 0\n}\n\nfunc (x *DependencyLink) GetSource() string {\n\tif x != nil {\n\t\treturn x.Source\n\t}\n\treturn \"\"\n}\n\ntype Trace_ProcessMapping struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tProcessId string   `protobuf:\"bytes,1,opt,name=process_id,json=processId,proto3\" json:\"process_id,omitempty\"`\n\tProcess   *Process `protobuf:\"bytes,2,opt,name=process,proto3\" json:\"process,omitempty\"`\n}\n\nfunc (x *Trace_ProcessMapping) Reset() {\n\t*x = Trace_ProcessMapping{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_model_model_proto_msgTypes[8]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Trace_ProcessMapping) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Trace_ProcessMapping) ProtoMessage() {}\n\nfunc (x *Trace_ProcessMapping) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_model_model_proto_msgTypes[8]\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 Trace_ProcessMapping.ProtoReflect.Descriptor instead.\nfunc (*Trace_ProcessMapping) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_model_model_proto_rawDescGZIP(), []int{5, 0}\n}\n\nfunc (x *Trace_ProcessMapping) GetProcessId() string {\n\tif x != nil {\n\t\treturn x.ProcessId\n\t}\n\treturn \"\"\n}\n\nfunc (x *Trace_ProcessMapping) GetProcess() *Process {\n\tif x != nil {\n\t\treturn x.Process\n\t}\n\treturn nil\n}\n\nvar File_jaeger_model_model_proto protoreflect.FileDescriptor\n\nvar file_jaeger_model_model_proto_rawDesc = []byte{\n\t0x0a, 0x18, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2f, 0x6d,\n\t0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x6a, 0x61, 0x65, 0x67,\n\t0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x1a, 0x14, 0x67, 0x6f, 0x67, 0x6f, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x67, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,\n\t0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,\n\t0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,\n\t0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x22, 0xd4, 0x01, 0x0a, 0x08, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x10, 0x0a,\n\t0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,\n\t0x2f, 0x0a, 0x06, 0x76, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32,\n\t0x18, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e,\n\t0x56, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x05, 0x76, 0x54, 0x79, 0x70, 0x65,\n\t0x12, 0x13, 0x0a, 0x05, 0x76, 0x5f, 0x73, 0x74, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,\n\t0x04, 0x76, 0x53, 0x74, 0x72, 0x12, 0x15, 0x0a, 0x06, 0x76, 0x5f, 0x62, 0x6f, 0x6f, 0x6c, 0x18,\n\t0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x42, 0x6f, 0x6f, 0x6c, 0x12, 0x17, 0x0a, 0x07,\n\t0x76, 0x5f, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x76,\n\t0x49, 0x6e, 0x74, 0x36, 0x34, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74,\n\t0x36, 0x34, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x76, 0x46, 0x6c, 0x6f, 0x61, 0x74,\n\t0x36, 0x34, 0x12, 0x19, 0x0a, 0x08, 0x76, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x18, 0x07,\n\t0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x76, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x3a, 0x08, 0xe8,\n\t0xa0, 0x1f, 0x01, 0xe8, 0xa1, 0x1f, 0x01, 0x22, 0x80, 0x01, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12,\n\t0x42, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x08,\n\t0xc8, 0xde, 0x1f, 0x00, 0x90, 0xdf, 0x1f, 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,\n\t0x61, 0x6d, 0x70, 0x12, 0x35, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x02, 0x20,\n\t0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69,\n\t0x5f, 0x76, 0x32, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x04, 0xc8, 0xde,\n\t0x1f, 0x00, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0xaa, 0x01, 0x0a, 0x07, 0x53,\n\t0x70, 0x61, 0x6e, 0x52, 0x65, 0x66, 0x12, 0x35, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f,\n\t0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x1a, 0xc8, 0xde, 0x1f, 0x00, 0xda, 0xde,\n\t0x1f, 0x07, 0x54, 0x72, 0x61, 0x63, 0x65, 0x49, 0x44, 0xe2, 0xde, 0x1f, 0x07, 0x54, 0x72, 0x61,\n\t0x63, 0x65, 0x49, 0x44, 0x52, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x31, 0x0a,\n\t0x07, 0x73, 0x70, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x18,\n\t0xc8, 0xde, 0x1f, 0x00, 0xda, 0xde, 0x1f, 0x06, 0x53, 0x70, 0x61, 0x6e, 0x49, 0x44, 0xe2, 0xde,\n\t0x1f, 0x06, 0x53, 0x70, 0x61, 0x6e, 0x49, 0x44, 0x52, 0x06, 0x73, 0x70, 0x61, 0x6e, 0x49, 0x64,\n\t0x12, 0x35, 0x0a, 0x08, 0x72, 0x65, 0x66, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01,\n\t0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f,\n\t0x76, 0x32, 0x2e, 0x53, 0x70, 0x61, 0x6e, 0x52, 0x65, 0x66, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07,\n\t0x72, 0x65, 0x66, 0x54, 0x79, 0x70, 0x65, 0x22, 0x5f, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x63, 0x65,\n\t0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61,\n\t0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,\n\t0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20,\n\t0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69,\n\t0x5f, 0x76, 0x32, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x04, 0xc8, 0xde,\n\t0x1f, 0x00, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x22, 0xdd, 0x04, 0x0a, 0x04, 0x53, 0x70, 0x61,\n\t0x6e, 0x12, 0x35, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x0c, 0x42, 0x1a, 0xc8, 0xde, 0x1f, 0x00, 0xda, 0xde, 0x1f, 0x07, 0x54, 0x72, 0x61,\n\t0x63, 0x65, 0x49, 0x44, 0xe2, 0xde, 0x1f, 0x07, 0x54, 0x72, 0x61, 0x63, 0x65, 0x49, 0x44, 0x52,\n\t0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x07, 0x73, 0x70, 0x61, 0x6e,\n\t0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x18, 0xc8, 0xde, 0x1f, 0x00, 0xda,\n\t0xde, 0x1f, 0x06, 0x53, 0x70, 0x61, 0x6e, 0x49, 0x44, 0xe2, 0xde, 0x1f, 0x06, 0x53, 0x70, 0x61,\n\t0x6e, 0x49, 0x44, 0x52, 0x06, 0x73, 0x70, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x6f,\n\t0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20,\n\t0x01, 0x28, 0x09, 0x52, 0x0d, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61,\n\t0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73,\n\t0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e,\n\t0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e, 0x53, 0x70, 0x61, 0x6e, 0x52, 0x65, 0x66, 0x42, 0x04,\n\t0xc8, 0xde, 0x1f, 0x00, 0x52, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73,\n\t0x12, 0x23, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x42,\n\t0x0d, 0xc8, 0xde, 0x1f, 0x00, 0xda, 0xde, 0x1f, 0x05, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x05,\n\t0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x43, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74,\n\t0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,\n\t0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65,\n\t0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x08, 0xc8, 0xde, 0x1f, 0x00, 0x90, 0xdf, 0x1f, 0x01, 0x52,\n\t0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x08, 0x64, 0x75,\n\t0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67,\n\t0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44,\n\t0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xc8, 0xde, 0x1f, 0x00, 0x98, 0xdf, 0x1f,\n\t0x01, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x31, 0x0a, 0x04, 0x74,\n\t0x61, 0x67, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6a, 0x61, 0x65, 0x67,\n\t0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c,\n\t0x75, 0x65, 0x42, 0x04, 0xc8, 0xde, 0x1f, 0x00, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x2c,\n\t0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6a,\n\t0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e, 0x4c, 0x6f, 0x67,\n\t0x42, 0x04, 0xc8, 0xde, 0x1f, 0x00, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x30, 0x0a, 0x07,\n\t0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e,\n\t0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e, 0x50, 0x72,\n\t0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x2c,\n\t0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x0b, 0x20, 0x01,\n\t0x28, 0x09, 0x42, 0x0d, 0xe2, 0xde, 0x1f, 0x09, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49,\n\t0x44, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08,\n\t0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08,\n\t0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x92, 0x02, 0x0a, 0x05, 0x54, 0x72, 0x61,\n\t0x63, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x73, 0x70, 0x61, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,\n\t0x0b, 0x32, 0x13, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76,\n\t0x32, 0x2e, 0x53, 0x70, 0x61, 0x6e, 0x52, 0x05, 0x73, 0x70, 0x61, 0x6e, 0x73, 0x12, 0x4a, 0x0a,\n\t0x0b, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x02, 0x20, 0x03,\n\t0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f,\n\t0x76, 0x32, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,\n\t0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x42, 0x04, 0xc8, 0xde, 0x1f, 0x00, 0x52, 0x0a, 0x70,\n\t0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x61, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x61, 0x72,\n\t0x6e, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x77, 0x61, 0x72,\n\t0x6e, 0x69, 0x6e, 0x67, 0x73, 0x1a, 0x76, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,\n\t0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x2c, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x63, 0x65,\n\t0x73, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0d, 0xe2, 0xde, 0x1f,\n\t0x09, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x44, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x63,\n\t0x65, 0x73, 0x73, 0x49, 0x64, 0x12, 0x36, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,\n\t0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e,\n\t0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x42, 0x04,\n\t0xc8, 0xde, 0x1f, 0x00, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x22, 0x6a, 0x0a,\n\t0x05, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x29, 0x0a, 0x05, 0x73, 0x70, 0x61, 0x6e, 0x73, 0x18,\n\t0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61,\n\t0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e, 0x53, 0x70, 0x61, 0x6e, 0x52, 0x05, 0x73, 0x70, 0x61, 0x6e,\n\t0x73, 0x12, 0x36, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01,\n\t0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f,\n\t0x76, 0x32, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x42, 0x04, 0xc8, 0xde, 0x1f, 0x01,\n\t0x52, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x22, 0x75, 0x0a, 0x0e, 0x44, 0x65, 0x70,\n\t0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x16, 0x0a, 0x06, 0x70,\n\t0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, 0x72,\n\t0x65, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01,\n\t0x28, 0x09, 0x52, 0x05, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x6c,\n\t0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x63,\n\t0x61, 0x6c, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72,\n\t0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,\n\t0x2a, 0x45, 0x0a, 0x09, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a,\n\t0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x42, 0x4f, 0x4f,\n\t0x4c, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, 0x02, 0x12, 0x0b,\n\t0x0a, 0x07, 0x46, 0x4c, 0x4f, 0x41, 0x54, 0x36, 0x34, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x42,\n\t0x49, 0x4e, 0x41, 0x52, 0x59, 0x10, 0x04, 0x2a, 0x2d, 0x0a, 0x0b, 0x53, 0x70, 0x61, 0x6e, 0x52,\n\t0x65, 0x66, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x48, 0x49, 0x4c, 0x44, 0x5f,\n\t0x4f, 0x46, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x46, 0x4f, 0x4c, 0x4c, 0x4f, 0x57, 0x53, 0x5f,\n\t0x46, 0x52, 0x4f, 0x4d, 0x10, 0x01, 0x42, 0x71, 0x0a, 0x17, 0x69, 0x6f, 0x2e, 0x6a, 0x61, 0x65,\n\t0x67, 0x65, 0x72, 0x74, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76,\n\t0x32, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x61,\n\t0x63, 0x6b, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x2f, 0x47, 0x6f,\n\t0x2d, 0x66, 0x6f, 0x72, 0x2d, 0x44, 0x65, 0x76, 0x4f, 0x70, 0x73, 0x2f, 0x63, 0x68, 0x61, 0x70,\n\t0x74, 0x65, 0x72, 0x2f, 0x31, 0x30, 0x2f, 0x6f, 0x70, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x2f, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0xc8, 0xe2, 0x1e,\n\t0x01, 0xd0, 0xe2, 0x1e, 0x01, 0xe0, 0xe2, 0x1e, 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x33,\n}\n\nvar (\n\tfile_jaeger_model_model_proto_rawDescOnce sync.Once\n\tfile_jaeger_model_model_proto_rawDescData = file_jaeger_model_model_proto_rawDesc\n)\n\nfunc file_jaeger_model_model_proto_rawDescGZIP() []byte {\n\tfile_jaeger_model_model_proto_rawDescOnce.Do(func() {\n\t\tfile_jaeger_model_model_proto_rawDescData = protoimpl.X.CompressGZIP(file_jaeger_model_model_proto_rawDescData)\n\t})\n\treturn file_jaeger_model_model_proto_rawDescData\n}\n\nvar file_jaeger_model_model_proto_enumTypes = make([]protoimpl.EnumInfo, 2)\nvar file_jaeger_model_model_proto_msgTypes = make([]protoimpl.MessageInfo, 9)\nvar file_jaeger_model_model_proto_goTypes = []interface{}{\n\t(ValueType)(0),                // 0: jaeger.api_v2.ValueType\n\t(SpanRefType)(0),              // 1: jaeger.api_v2.SpanRefType\n\t(*KeyValue)(nil),              // 2: jaeger.api_v2.KeyValue\n\t(*Log)(nil),                   // 3: jaeger.api_v2.Log\n\t(*SpanRef)(nil),               // 4: jaeger.api_v2.SpanRef\n\t(*Process)(nil),               // 5: jaeger.api_v2.Process\n\t(*Span)(nil),                  // 6: jaeger.api_v2.Span\n\t(*Trace)(nil),                 // 7: jaeger.api_v2.Trace\n\t(*Batch)(nil),                 // 8: jaeger.api_v2.Batch\n\t(*DependencyLink)(nil),        // 9: jaeger.api_v2.DependencyLink\n\t(*Trace_ProcessMapping)(nil),  // 10: jaeger.api_v2.Trace.ProcessMapping\n\t(*timestamppb.Timestamp)(nil), // 11: google.protobuf.Timestamp\n\t(*durationpb.Duration)(nil),   // 12: google.protobuf.Duration\n}\nvar file_jaeger_model_model_proto_depIdxs = []int32{\n\t0,  // 0: jaeger.api_v2.KeyValue.v_type:type_name -> jaeger.api_v2.ValueType\n\t11, // 1: jaeger.api_v2.Log.timestamp:type_name -> google.protobuf.Timestamp\n\t2,  // 2: jaeger.api_v2.Log.fields:type_name -> jaeger.api_v2.KeyValue\n\t1,  // 3: jaeger.api_v2.SpanRef.ref_type:type_name -> jaeger.api_v2.SpanRefType\n\t2,  // 4: jaeger.api_v2.Process.tags:type_name -> jaeger.api_v2.KeyValue\n\t4,  // 5: jaeger.api_v2.Span.references:type_name -> jaeger.api_v2.SpanRef\n\t11, // 6: jaeger.api_v2.Span.start_time:type_name -> google.protobuf.Timestamp\n\t12, // 7: jaeger.api_v2.Span.duration:type_name -> google.protobuf.Duration\n\t2,  // 8: jaeger.api_v2.Span.tags:type_name -> jaeger.api_v2.KeyValue\n\t3,  // 9: jaeger.api_v2.Span.logs:type_name -> jaeger.api_v2.Log\n\t5,  // 10: jaeger.api_v2.Span.process:type_name -> jaeger.api_v2.Process\n\t6,  // 11: jaeger.api_v2.Trace.spans:type_name -> jaeger.api_v2.Span\n\t10, // 12: jaeger.api_v2.Trace.process_map:type_name -> jaeger.api_v2.Trace.ProcessMapping\n\t6,  // 13: jaeger.api_v2.Batch.spans:type_name -> jaeger.api_v2.Span\n\t5,  // 14: jaeger.api_v2.Batch.process:type_name -> jaeger.api_v2.Process\n\t5,  // 15: jaeger.api_v2.Trace.ProcessMapping.process:type_name -> jaeger.api_v2.Process\n\t16, // [16:16] is the sub-list for method output_type\n\t16, // [16:16] is the sub-list for method input_type\n\t16, // [16:16] is the sub-list for extension type_name\n\t16, // [16:16] is the sub-list for extension extendee\n\t0,  // [0:16] is the sub-list for field type_name\n}\n\nfunc init() { file_jaeger_model_model_proto_init() }\nfunc file_jaeger_model_model_proto_init() {\n\tif File_jaeger_model_model_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_jaeger_model_model_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*KeyValue); 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_jaeger_model_model_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Log); 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_jaeger_model_model_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*SpanRef); 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_jaeger_model_model_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Process); 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_jaeger_model_model_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Span); 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_jaeger_model_model_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Trace); 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_jaeger_model_model_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Batch); 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_jaeger_model_model_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DependencyLink); 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_jaeger_model_model_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Trace_ProcessMapping); 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_jaeger_model_model_proto_rawDesc,\n\t\t\tNumEnums:      2,\n\t\t\tNumMessages:   9,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_jaeger_model_model_proto_goTypes,\n\t\tDependencyIndexes: file_jaeger_model_model_proto_depIdxs,\n\t\tEnumInfos:         file_jaeger_model_model_proto_enumTypes,\n\t\tMessageInfos:      file_jaeger_model_model_proto_msgTypes,\n\t}.Build()\n\tFile_jaeger_model_model_proto = out.File\n\tfile_jaeger_model_model_proto_rawDesc = nil\n\tfile_jaeger_model_model_proto_goTypes = nil\n\tfile_jaeger_model_model_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "chapter/11/ops/proto/jaeger/model/model.proto",
    "content": "// Copyright (c) 2018 Uber Technologies, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax=\"proto3\";\n\npackage jaeger.api_v2;\n\nimport \"gogoproto/gogo.proto\";\nimport \"google/protobuf/timestamp.proto\";\nimport \"google/protobuf/duration.proto\";\n\n// TODO: document all types and fields\n\n// TODO: once this moves to jaeger-idl repo, we may want to change Go pkg to api_v2\n// and rewrite it to model only in this repo. That should make it easier to generate\n// classes in other languages.\noption go_package = \"github.com/PacktPublishing/Go-for-DevOps/chapter/10/ops/proto/jaeger/model\";\noption java_package = \"io.jaegertracing.api_v2\";\n\n// Enable gogoprotobuf extensions (https://github.com/gogo/protobuf/blob/master/extensions.md).\n// Enable custom Marshal method.\noption (gogoproto.marshaler_all) = true;\n// Enable custom Unmarshal method.\noption (gogoproto.unmarshaler_all) = true;\n// Enable custom Size method (Required by Marshal and Unmarshal).\noption (gogoproto.sizer_all) = true;\n\nenum ValueType {\n  STRING  = 0;\n  BOOL    = 1;\n  INT64   = 2;\n  FLOAT64 = 3;\n  BINARY  = 4;\n};\n\nmessage KeyValue {\n  option (gogoproto.equal) = true;\n  option (gogoproto.compare) = true;\n\n  string    key      = 1;\n  ValueType v_type    = 2;\n  string    v_str     = 3;\n  bool      v_bool    = 4;\n  int64     v_int64   = 5;\n  double    v_float64 = 6;\n  bytes     v_binary  = 7;\n}\n\nmessage Log {\n  google.protobuf.Timestamp timestamp = 1 [\n    (gogoproto.stdtime) = true,\n    (gogoproto.nullable) = false\n  ];\n  repeated KeyValue fields = 2 [\n    (gogoproto.nullable) = false\n  ];\n}\n\nenum SpanRefType {\n  CHILD_OF = 0;\n  FOLLOWS_FROM = 1;\n};\n\nmessage SpanRef {\n  bytes trace_id = 1 [\n    (gogoproto.nullable) = false,\n    (gogoproto.customtype) = \"TraceID\",\n    (gogoproto.customname) = \"TraceID\"\n  ];\n  bytes span_id = 2 [\n    (gogoproto.nullable) = false,\n    (gogoproto.customtype) = \"SpanID\",\n    (gogoproto.customname) = \"SpanID\"\n  ];\n  SpanRefType ref_type = 3;\n}\n\nmessage Process {\n  string service_name = 1;\n  repeated KeyValue tags = 2 [\n    (gogoproto.nullable) = false\n  ];\n}\n\nmessage Span {\n  bytes trace_id = 1 [\n    (gogoproto.nullable) = false,\n    (gogoproto.customtype) = \"TraceID\",\n    (gogoproto.customname) = \"TraceID\"\n  ];\n  bytes span_id = 2 [\n    (gogoproto.nullable) = false,\n    (gogoproto.customtype) = \"SpanID\",\n    (gogoproto.customname) = \"SpanID\"\n  ];\n  string operation_name = 3;\n  repeated SpanRef references = 4 [\n    (gogoproto.nullable) = false\n  ];\n  uint32 flags = 5 [\n    (gogoproto.nullable) = false,\n    (gogoproto.customtype) = \"Flags\"\n  ];\n  google.protobuf.Timestamp start_time = 6 [\n    (gogoproto.stdtime) = true,\n    (gogoproto.nullable) = false\n  ];\n  google.protobuf.Duration duration = 7 [\n    (gogoproto.stdduration) = true,\n    (gogoproto.nullable) = false\n  ];\n  repeated KeyValue tags = 8 [\n    (gogoproto.nullable) = false\n  ];\n  repeated Log logs = 9 [\n    (gogoproto.nullable) = false\n  ];\n  Process process = 10;\n  string process_id = 11 [\n    (gogoproto.customname) = \"ProcessID\"\n  ];\n  repeated string warnings = 12;\n}\n\nmessage Trace {\n  message ProcessMapping {\n      string process_id = 1 [\n        (gogoproto.customname) = \"ProcessID\"\n      ];\n      Process process = 2 [\n        (gogoproto.nullable) = false\n      ];\n  }\n  repeated Span spans = 1;\n  repeated ProcessMapping process_map = 2 [\n    (gogoproto.nullable) = false\n  ];\n  repeated string warnings = 3;\n}\n\n// Note that both Span and Batch may contain a Process.\n// This is different from the Thrift model which was only used\n// for transport, because Proto model is also used by the backend\n// as the domain model, where once a batch is received it is split\n// into individual spans which are all processed independently,\n// and therefore they all need a Process. As far as on-the-wire\n// semantics, both Batch and Spans in the same message may contain\n// their own instances of Process, with span.Process taking priority\n// over batch.Process.\nmessage Batch {\n    repeated Span spans = 1;\n    Process process = 2 [\n      (gogoproto.nullable) = true\n    ];\n}\n\nmessage DependencyLink {\n  string parent = 1;\n  string child = 2;\n  uint64 call_count = 3;\n  string source = 4;\n}\n"
  },
  {
    "path": "chapter/11/ops/proto/jaeger/query.pb.go",
    "content": "// Copyright (c) 2019 The Jaeger Authors.\n// Copyright (c) 2018 Uber Technologies, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.25.0-devel\n// \tprotoc        v3.18.0\n// source: jaeger/query.proto\n\npackage jaeger\n\nimport (\n\tmodel \"github.com/PacktPublishing/Go-for-DevOps/chapter/11/ops/proto/jaeger/model\"\n\t_ \"github.com/gogo/protobuf/gogoproto\"\n\t_ \"google.golang.org/genproto/googleapis/api/annotations\"\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\tdurationpb \"google.golang.org/protobuf/types/known/durationpb\"\n\ttimestamppb \"google.golang.org/protobuf/types/known/timestamppb\"\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 GetTraceRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tTraceId []byte `protobuf:\"bytes,1,opt,name=trace_id,json=traceId,proto3\" json:\"trace_id,omitempty\"`\n}\n\nfunc (x *GetTraceRequest) Reset() {\n\t*x = GetTraceRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_query_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GetTraceRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GetTraceRequest) ProtoMessage() {}\n\nfunc (x *GetTraceRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_query_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 GetTraceRequest.ProtoReflect.Descriptor instead.\nfunc (*GetTraceRequest) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_query_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *GetTraceRequest) GetTraceId() []byte {\n\tif x != nil {\n\t\treturn x.TraceId\n\t}\n\treturn nil\n}\n\ntype SpansResponseChunk struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tSpans []*model.Span `protobuf:\"bytes,1,rep,name=spans,proto3\" json:\"spans,omitempty\"`\n}\n\nfunc (x *SpansResponseChunk) Reset() {\n\t*x = SpansResponseChunk{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_query_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *SpansResponseChunk) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*SpansResponseChunk) ProtoMessage() {}\n\nfunc (x *SpansResponseChunk) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_query_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 SpansResponseChunk.ProtoReflect.Descriptor instead.\nfunc (*SpansResponseChunk) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_query_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *SpansResponseChunk) GetSpans() []*model.Span {\n\tif x != nil {\n\t\treturn x.Spans\n\t}\n\treturn nil\n}\n\ntype ArchiveTraceRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tTraceId []byte `protobuf:\"bytes,1,opt,name=trace_id,json=traceId,proto3\" json:\"trace_id,omitempty\"`\n}\n\nfunc (x *ArchiveTraceRequest) Reset() {\n\t*x = ArchiveTraceRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_query_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ArchiveTraceRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ArchiveTraceRequest) ProtoMessage() {}\n\nfunc (x *ArchiveTraceRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_query_proto_msgTypes[2]\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 ArchiveTraceRequest.ProtoReflect.Descriptor instead.\nfunc (*ArchiveTraceRequest) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_query_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *ArchiveTraceRequest) GetTraceId() []byte {\n\tif x != nil {\n\t\treturn x.TraceId\n\t}\n\treturn nil\n}\n\ntype ArchiveTraceResponse struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ArchiveTraceResponse) Reset() {\n\t*x = ArchiveTraceResponse{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_query_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ArchiveTraceResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ArchiveTraceResponse) ProtoMessage() {}\n\nfunc (x *ArchiveTraceResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_query_proto_msgTypes[3]\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 ArchiveTraceResponse.ProtoReflect.Descriptor instead.\nfunc (*ArchiveTraceResponse) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_query_proto_rawDescGZIP(), []int{3}\n}\n\ntype TraceQueryParameters struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tServiceName   string                 `protobuf:\"bytes,1,opt,name=service_name,json=serviceName,proto3\" json:\"service_name,omitempty\"`\n\tOperationName string                 `protobuf:\"bytes,2,opt,name=operation_name,json=operationName,proto3\" json:\"operation_name,omitempty\"`\n\tTags          map[string]string      `protobuf:\"bytes,3,rep,name=tags,proto3\" json:\"tags,omitempty\" protobuf_key:\"bytes,1,opt,name=key,proto3\" protobuf_val:\"bytes,2,opt,name=value,proto3\"`\n\tStartTimeMin  *timestamppb.Timestamp `protobuf:\"bytes,4,opt,name=start_time_min,json=startTimeMin,proto3\" json:\"start_time_min,omitempty\"`\n\tStartTimeMax  *timestamppb.Timestamp `protobuf:\"bytes,5,opt,name=start_time_max,json=startTimeMax,proto3\" json:\"start_time_max,omitempty\"`\n\tDurationMin   *durationpb.Duration   `protobuf:\"bytes,6,opt,name=duration_min,json=durationMin,proto3\" json:\"duration_min,omitempty\"`\n\tDurationMax   *durationpb.Duration   `protobuf:\"bytes,7,opt,name=duration_max,json=durationMax,proto3\" json:\"duration_max,omitempty\"`\n\tSearchDepth   int32                  `protobuf:\"varint,8,opt,name=search_depth,json=searchDepth,proto3\" json:\"search_depth,omitempty\"`\n}\n\nfunc (x *TraceQueryParameters) Reset() {\n\t*x = TraceQueryParameters{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_query_proto_msgTypes[4]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *TraceQueryParameters) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*TraceQueryParameters) ProtoMessage() {}\n\nfunc (x *TraceQueryParameters) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_query_proto_msgTypes[4]\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 TraceQueryParameters.ProtoReflect.Descriptor instead.\nfunc (*TraceQueryParameters) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_query_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (x *TraceQueryParameters) GetServiceName() string {\n\tif x != nil {\n\t\treturn x.ServiceName\n\t}\n\treturn \"\"\n}\n\nfunc (x *TraceQueryParameters) GetOperationName() string {\n\tif x != nil {\n\t\treturn x.OperationName\n\t}\n\treturn \"\"\n}\n\nfunc (x *TraceQueryParameters) GetTags() map[string]string {\n\tif x != nil {\n\t\treturn x.Tags\n\t}\n\treturn nil\n}\n\nfunc (x *TraceQueryParameters) GetStartTimeMin() *timestamppb.Timestamp {\n\tif x != nil {\n\t\treturn x.StartTimeMin\n\t}\n\treturn nil\n}\n\nfunc (x *TraceQueryParameters) GetStartTimeMax() *timestamppb.Timestamp {\n\tif x != nil {\n\t\treturn x.StartTimeMax\n\t}\n\treturn nil\n}\n\nfunc (x *TraceQueryParameters) GetDurationMin() *durationpb.Duration {\n\tif x != nil {\n\t\treturn x.DurationMin\n\t}\n\treturn nil\n}\n\nfunc (x *TraceQueryParameters) GetDurationMax() *durationpb.Duration {\n\tif x != nil {\n\t\treturn x.DurationMax\n\t}\n\treturn nil\n}\n\nfunc (x *TraceQueryParameters) GetSearchDepth() int32 {\n\tif x != nil {\n\t\treturn x.SearchDepth\n\t}\n\treturn 0\n}\n\ntype FindTracesRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tQuery *TraceQueryParameters `protobuf:\"bytes,1,opt,name=query,proto3\" json:\"query,omitempty\"`\n}\n\nfunc (x *FindTracesRequest) Reset() {\n\t*x = FindTracesRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_query_proto_msgTypes[5]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *FindTracesRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*FindTracesRequest) ProtoMessage() {}\n\nfunc (x *FindTracesRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_query_proto_msgTypes[5]\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 FindTracesRequest.ProtoReflect.Descriptor instead.\nfunc (*FindTracesRequest) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_query_proto_rawDescGZIP(), []int{5}\n}\n\nfunc (x *FindTracesRequest) GetQuery() *TraceQueryParameters {\n\tif x != nil {\n\t\treturn x.Query\n\t}\n\treturn nil\n}\n\ntype GetServicesRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *GetServicesRequest) Reset() {\n\t*x = GetServicesRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_query_proto_msgTypes[6]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GetServicesRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GetServicesRequest) ProtoMessage() {}\n\nfunc (x *GetServicesRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_query_proto_msgTypes[6]\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 GetServicesRequest.ProtoReflect.Descriptor instead.\nfunc (*GetServicesRequest) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_query_proto_rawDescGZIP(), []int{6}\n}\n\ntype GetServicesResponse struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tServices []string `protobuf:\"bytes,1,rep,name=services,proto3\" json:\"services,omitempty\"`\n}\n\nfunc (x *GetServicesResponse) Reset() {\n\t*x = GetServicesResponse{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_query_proto_msgTypes[7]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GetServicesResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GetServicesResponse) ProtoMessage() {}\n\nfunc (x *GetServicesResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_query_proto_msgTypes[7]\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 GetServicesResponse.ProtoReflect.Descriptor instead.\nfunc (*GetServicesResponse) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_query_proto_rawDescGZIP(), []int{7}\n}\n\nfunc (x *GetServicesResponse) GetServices() []string {\n\tif x != nil {\n\t\treturn x.Services\n\t}\n\treturn nil\n}\n\ntype GetOperationsRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tService  string `protobuf:\"bytes,1,opt,name=service,proto3\" json:\"service,omitempty\"`\n\tSpanKind string `protobuf:\"bytes,2,opt,name=span_kind,json=spanKind,proto3\" json:\"span_kind,omitempty\"`\n}\n\nfunc (x *GetOperationsRequest) Reset() {\n\t*x = GetOperationsRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_query_proto_msgTypes[8]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GetOperationsRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GetOperationsRequest) ProtoMessage() {}\n\nfunc (x *GetOperationsRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_query_proto_msgTypes[8]\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 GetOperationsRequest.ProtoReflect.Descriptor instead.\nfunc (*GetOperationsRequest) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_query_proto_rawDescGZIP(), []int{8}\n}\n\nfunc (x *GetOperationsRequest) GetService() string {\n\tif x != nil {\n\t\treturn x.Service\n\t}\n\treturn \"\"\n}\n\nfunc (x *GetOperationsRequest) GetSpanKind() string {\n\tif x != nil {\n\t\treturn x.SpanKind\n\t}\n\treturn \"\"\n}\n\ntype Operation struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tName     string `protobuf:\"bytes,1,opt,name=name,proto3\" json:\"name,omitempty\"`\n\tSpanKind string `protobuf:\"bytes,2,opt,name=span_kind,json=spanKind,proto3\" json:\"span_kind,omitempty\"`\n}\n\nfunc (x *Operation) Reset() {\n\t*x = Operation{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_query_proto_msgTypes[9]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Operation) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Operation) ProtoMessage() {}\n\nfunc (x *Operation) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_query_proto_msgTypes[9]\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 Operation.ProtoReflect.Descriptor instead.\nfunc (*Operation) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_query_proto_rawDescGZIP(), []int{9}\n}\n\nfunc (x *Operation) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\nfunc (x *Operation) GetSpanKind() string {\n\tif x != nil {\n\t\treturn x.SpanKind\n\t}\n\treturn \"\"\n}\n\ntype GetOperationsResponse struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tOperationNames []string     `protobuf:\"bytes,1,rep,name=operationNames,proto3\" json:\"operationNames,omitempty\"` //deprecated\n\tOperations     []*Operation `protobuf:\"bytes,2,rep,name=operations,proto3\" json:\"operations,omitempty\"`\n}\n\nfunc (x *GetOperationsResponse) Reset() {\n\t*x = GetOperationsResponse{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_query_proto_msgTypes[10]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GetOperationsResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GetOperationsResponse) ProtoMessage() {}\n\nfunc (x *GetOperationsResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_query_proto_msgTypes[10]\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 GetOperationsResponse.ProtoReflect.Descriptor instead.\nfunc (*GetOperationsResponse) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_query_proto_rawDescGZIP(), []int{10}\n}\n\nfunc (x *GetOperationsResponse) GetOperationNames() []string {\n\tif x != nil {\n\t\treturn x.OperationNames\n\t}\n\treturn nil\n}\n\nfunc (x *GetOperationsResponse) GetOperations() []*Operation {\n\tif x != nil {\n\t\treturn x.Operations\n\t}\n\treturn nil\n}\n\ntype GetDependenciesRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tStartTime *timestamppb.Timestamp `protobuf:\"bytes,1,opt,name=start_time,json=startTime,proto3\" json:\"start_time,omitempty\"`\n\tEndTime   *timestamppb.Timestamp `protobuf:\"bytes,2,opt,name=end_time,json=endTime,proto3\" json:\"end_time,omitempty\"`\n}\n\nfunc (x *GetDependenciesRequest) Reset() {\n\t*x = GetDependenciesRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_query_proto_msgTypes[11]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GetDependenciesRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GetDependenciesRequest) ProtoMessage() {}\n\nfunc (x *GetDependenciesRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_query_proto_msgTypes[11]\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 GetDependenciesRequest.ProtoReflect.Descriptor instead.\nfunc (*GetDependenciesRequest) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_query_proto_rawDescGZIP(), []int{11}\n}\n\nfunc (x *GetDependenciesRequest) GetStartTime() *timestamppb.Timestamp {\n\tif x != nil {\n\t\treturn x.StartTime\n\t}\n\treturn nil\n}\n\nfunc (x *GetDependenciesRequest) GetEndTime() *timestamppb.Timestamp {\n\tif x != nil {\n\t\treturn x.EndTime\n\t}\n\treturn nil\n}\n\ntype GetDependenciesResponse struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tDependencies []*model.DependencyLink `protobuf:\"bytes,1,rep,name=dependencies,proto3\" json:\"dependencies,omitempty\"`\n}\n\nfunc (x *GetDependenciesResponse) Reset() {\n\t*x = GetDependenciesResponse{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_query_proto_msgTypes[12]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GetDependenciesResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GetDependenciesResponse) ProtoMessage() {}\n\nfunc (x *GetDependenciesResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_query_proto_msgTypes[12]\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 GetDependenciesResponse.ProtoReflect.Descriptor instead.\nfunc (*GetDependenciesResponse) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_query_proto_rawDescGZIP(), []int{12}\n}\n\nfunc (x *GetDependenciesResponse) GetDependencies() []*model.DependencyLink {\n\tif x != nil {\n\t\treturn x.Dependencies\n\t}\n\treturn nil\n}\n\nvar File_jaeger_query_proto protoreflect.FileDescriptor\n\nvar file_jaeger_query_proto_rawDesc = []byte{\n\t0x0a, 0x12, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69,\n\t0x5f, 0x76, 0x32, 0x1a, 0x18, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2f, 0x6d, 0x6f, 0x64, 0x65,\n\t0x6c, 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x67,\n\t0x6f, 0x67, 0x6f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x67, 0x6f, 0x2e, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f,\n\t0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,\n\t0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x22, 0x6e, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x65,\n\t0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5b, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x69,\n\t0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x40, 0xc8, 0xde, 0x1f, 0x00, 0xda, 0xde, 0x1f,\n\t0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x61, 0x65, 0x67,\n\t0x65, 0x72, 0x74, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x2f, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72,\n\t0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x49, 0x44, 0xe2, 0xde,\n\t0x1f, 0x07, 0x54, 0x72, 0x61, 0x63, 0x65, 0x49, 0x44, 0x52, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65,\n\t0x49, 0x64, 0x22, 0x45, 0x0a, 0x12, 0x53, 0x70, 0x61, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,\n\t0x6e, 0x73, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x2f, 0x0a, 0x05, 0x73, 0x70, 0x61, 0x6e,\n\t0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72,\n\t0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e, 0x53, 0x70, 0x61, 0x6e, 0x42, 0x04, 0xc8, 0xde,\n\t0x1f, 0x00, 0x52, 0x05, 0x73, 0x70, 0x61, 0x6e, 0x73, 0x22, 0x72, 0x0a, 0x13, 0x41, 0x72, 0x63,\n\t0x68, 0x69, 0x76, 0x65, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,\n\t0x12, 0x5b, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x0c, 0x42, 0x40, 0xc8, 0xde, 0x1f, 0x00, 0xda, 0xde, 0x1f, 0x2d, 0x67, 0x69, 0x74, 0x68,\n\t0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x74, 0x72, 0x61,\n\t0x63, 0x69, 0x6e, 0x67, 0x2f, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2f, 0x6d, 0x6f, 0x64, 0x65,\n\t0x6c, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x49, 0x44, 0xe2, 0xde, 0x1f, 0x07, 0x54, 0x72, 0x61,\n\t0x63, 0x65, 0x49, 0x44, 0x52, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x22, 0x16, 0x0a,\n\t0x14, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73,\n\t0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xa7, 0x04, 0x0a, 0x14, 0x54, 0x72, 0x61, 0x63, 0x65, 0x51,\n\t0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x21,\n\t0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,\n\t0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d,\n\t0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e,\n\t0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6f, 0x70, 0x65, 0x72, 0x61,\n\t0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x41, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73,\n\t0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e,\n\t0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x51, 0x75, 0x65, 0x72,\n\t0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x54, 0x61, 0x67, 0x73,\n\t0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x4a, 0x0a, 0x0e, 0x73,\n\t0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x04, 0x20,\n\t0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42,\n\t0x08, 0xc8, 0xde, 0x1f, 0x00, 0x90, 0xdf, 0x1f, 0x01, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x72, 0x74,\n\t0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x6e, 0x12, 0x4a, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74,\n\t0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32,\n\t0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,\n\t0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x08, 0xc8, 0xde, 0x1f,\n\t0x00, 0x90, 0xdf, 0x1f, 0x01, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65,\n\t0x4d, 0x61, 0x78, 0x12, 0x46, 0x0a, 0x0c, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f,\n\t0x6d, 0x69, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67,\n\t0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61,\n\t0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xc8, 0xde, 0x1f, 0x00, 0x98, 0xdf, 0x1f, 0x01, 0x52, 0x0b,\n\t0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x6e, 0x12, 0x46, 0x0a, 0x0c, 0x64,\n\t0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x07, 0x20, 0x01, 0x28,\n\t0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xc8, 0xde,\n\t0x1f, 0x00, 0x98, 0xdf, 0x1f, 0x01, 0x52, 0x0b, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,\n\t0x4d, 0x61, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x64, 0x65,\n\t0x70, 0x74, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x73, 0x65, 0x61, 0x72, 0x63,\n\t0x68, 0x44, 0x65, 0x70, 0x74, 0x68, 0x1a, 0x37, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e,\n\t0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,\n\t0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02,\n\t0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22,\n\t0x4e, 0x0a, 0x11, 0x46, 0x69, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69,\n\t0x5f, 0x76, 0x32, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61,\n\t0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22,\n\t0x14, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65,\n\t0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x31, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76,\n\t0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08,\n\t0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08,\n\t0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0x4d, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x4f,\n\t0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,\n\t0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70,\n\t0x61, 0x6e, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73,\n\t0x70, 0x61, 0x6e, 0x4b, 0x69, 0x6e, 0x64, 0x22, 0x3c, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x61,\n\t0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x61, 0x6e,\n\t0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x61,\n\t0x6e, 0x4b, 0x69, 0x6e, 0x64, 0x22, 0x79, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72,\n\t0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26,\n\t0x0a, 0x0e, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x73,\n\t0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f,\n\t0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,\n\t0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6a, 0x61, 0x65,\n\t0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61,\n\t0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,\n\t0x22, 0x9e, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e,\n\t0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x0a, 0x73,\n\t0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,\n\t0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,\n\t0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x08, 0xc8, 0xde, 0x1f,\n\t0x00, 0x90, 0xdf, 0x1f, 0x01, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65,\n\t0x12, 0x3f, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,\n\t0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x08,\n\t0xc8, 0xde, 0x1f, 0x00, 0x90, 0xdf, 0x1f, 0x01, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d,\n\t0x65, 0x22, 0x62, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e,\n\t0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c,\n\t0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,\n\t0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f,\n\t0x76, 0x32, 0x2e, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x4c, 0x69, 0x6e,\n\t0x6b, 0x42, 0x04, 0xc8, 0xde, 0x1f, 0x00, 0x52, 0x0c, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65,\n\t0x6e, 0x63, 0x69, 0x65, 0x73, 0x32, 0xad, 0x05, 0x0a, 0x0c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53,\n\t0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x6b, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61,\n\t0x63, 0x65, 0x12, 0x1e, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f,\n\t0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,\n\t0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f,\n\t0x76, 0x32, 0x2e, 0x53, 0x70, 0x61, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,\n\t0x43, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f,\n\t0x74, 0x72, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64,\n\t0x7d, 0x30, 0x01, 0x12, 0x74, 0x0a, 0x0c, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x54, 0x72,\n\t0x61, 0x63, 0x65, 0x12, 0x22, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69,\n\t0x5f, 0x76, 0x32, 0x2e, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x54, 0x72, 0x61, 0x63, 0x65,\n\t0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72,\n\t0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x54,\n\t0x72, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3,\n\t0xe4, 0x93, 0x02, 0x15, 0x22, 0x13, 0x2f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x2f, 0x7b,\n\t0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x67, 0x0a, 0x0a, 0x46, 0x69, 0x6e,\n\t0x64, 0x54, 0x72, 0x61, 0x63, 0x65, 0x73, 0x12, 0x20, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72,\n\t0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x63,\n\t0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6a, 0x61, 0x65, 0x67,\n\t0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e, 0x53, 0x70, 0x61, 0x6e, 0x73, 0x52,\n\t0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0x12, 0x82, 0xd3,\n\t0xe4, 0x93, 0x02, 0x0c, 0x22, 0x07, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x3a, 0x01, 0x2a,\n\t0x30, 0x01, 0x12, 0x67, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,\n\t0x73, 0x12, 0x21, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76,\n\t0x32, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70,\n\t0x69, 0x5f, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,\n\t0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x11, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0b,\n\t0x12, 0x09, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x6f, 0x0a, 0x0d, 0x47,\n\t0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, 0x2e, 0x6a,\n\t0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74,\n\t0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,\n\t0x74, 0x1a, 0x24, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76,\n\t0x32, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52,\n\t0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12,\n\t0x0b, 0x2f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x77, 0x0a, 0x0f,\n\t0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12,\n\t0x25, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e,\n\t0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52,\n\t0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e,\n\t0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64,\n\t0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15,\n\t0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65,\n\t0x6e, 0x63, 0x69, 0x65, 0x73, 0x42, 0x6b, 0x0a, 0x17, 0x69, 0x6f, 0x2e, 0x6a, 0x61, 0x65, 0x67,\n\t0x65, 0x72, 0x74, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32,\n\t0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x61, 0x63,\n\t0x6b, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x2f, 0x47, 0x6f, 0x2d,\n\t0x66, 0x6f, 0x72, 0x2d, 0x44, 0x65, 0x76, 0x4f, 0x70, 0x73, 0x2f, 0x63, 0x68, 0x61, 0x70, 0x74,\n\t0x65, 0x72, 0x2f, 0x31, 0x30, 0x2f, 0x6f, 0x70, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,\n\t0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0xc8, 0xe2, 0x1e, 0x01, 0xd0, 0xe2, 0x1e, 0x01, 0xe0, 0xe2,\n\t0x1e, 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_jaeger_query_proto_rawDescOnce sync.Once\n\tfile_jaeger_query_proto_rawDescData = file_jaeger_query_proto_rawDesc\n)\n\nfunc file_jaeger_query_proto_rawDescGZIP() []byte {\n\tfile_jaeger_query_proto_rawDescOnce.Do(func() {\n\t\tfile_jaeger_query_proto_rawDescData = protoimpl.X.CompressGZIP(file_jaeger_query_proto_rawDescData)\n\t})\n\treturn file_jaeger_query_proto_rawDescData\n}\n\nvar file_jaeger_query_proto_msgTypes = make([]protoimpl.MessageInfo, 14)\nvar file_jaeger_query_proto_goTypes = []interface{}{\n\t(*GetTraceRequest)(nil),         // 0: jaeger.api_v2.GetTraceRequest\n\t(*SpansResponseChunk)(nil),      // 1: jaeger.api_v2.SpansResponseChunk\n\t(*ArchiveTraceRequest)(nil),     // 2: jaeger.api_v2.ArchiveTraceRequest\n\t(*ArchiveTraceResponse)(nil),    // 3: jaeger.api_v2.ArchiveTraceResponse\n\t(*TraceQueryParameters)(nil),    // 4: jaeger.api_v2.TraceQueryParameters\n\t(*FindTracesRequest)(nil),       // 5: jaeger.api_v2.FindTracesRequest\n\t(*GetServicesRequest)(nil),      // 6: jaeger.api_v2.GetServicesRequest\n\t(*GetServicesResponse)(nil),     // 7: jaeger.api_v2.GetServicesResponse\n\t(*GetOperationsRequest)(nil),    // 8: jaeger.api_v2.GetOperationsRequest\n\t(*Operation)(nil),               // 9: jaeger.api_v2.Operation\n\t(*GetOperationsResponse)(nil),   // 10: jaeger.api_v2.GetOperationsResponse\n\t(*GetDependenciesRequest)(nil),  // 11: jaeger.api_v2.GetDependenciesRequest\n\t(*GetDependenciesResponse)(nil), // 12: jaeger.api_v2.GetDependenciesResponse\n\tnil,                             // 13: jaeger.api_v2.TraceQueryParameters.TagsEntry\n\t(*model.Span)(nil),              // 14: jaeger.api_v2.Span\n\t(*timestamppb.Timestamp)(nil),   // 15: google.protobuf.Timestamp\n\t(*durationpb.Duration)(nil),     // 16: google.protobuf.Duration\n\t(*model.DependencyLink)(nil),    // 17: jaeger.api_v2.DependencyLink\n}\nvar file_jaeger_query_proto_depIdxs = []int32{\n\t14, // 0: jaeger.api_v2.SpansResponseChunk.spans:type_name -> jaeger.api_v2.Span\n\t13, // 1: jaeger.api_v2.TraceQueryParameters.tags:type_name -> jaeger.api_v2.TraceQueryParameters.TagsEntry\n\t15, // 2: jaeger.api_v2.TraceQueryParameters.start_time_min:type_name -> google.protobuf.Timestamp\n\t15, // 3: jaeger.api_v2.TraceQueryParameters.start_time_max:type_name -> google.protobuf.Timestamp\n\t16, // 4: jaeger.api_v2.TraceQueryParameters.duration_min:type_name -> google.protobuf.Duration\n\t16, // 5: jaeger.api_v2.TraceQueryParameters.duration_max:type_name -> google.protobuf.Duration\n\t4,  // 6: jaeger.api_v2.FindTracesRequest.query:type_name -> jaeger.api_v2.TraceQueryParameters\n\t9,  // 7: jaeger.api_v2.GetOperationsResponse.operations:type_name -> jaeger.api_v2.Operation\n\t15, // 8: jaeger.api_v2.GetDependenciesRequest.start_time:type_name -> google.protobuf.Timestamp\n\t15, // 9: jaeger.api_v2.GetDependenciesRequest.end_time:type_name -> google.protobuf.Timestamp\n\t17, // 10: jaeger.api_v2.GetDependenciesResponse.dependencies:type_name -> jaeger.api_v2.DependencyLink\n\t0,  // 11: jaeger.api_v2.QueryService.GetTrace:input_type -> jaeger.api_v2.GetTraceRequest\n\t2,  // 12: jaeger.api_v2.QueryService.ArchiveTrace:input_type -> jaeger.api_v2.ArchiveTraceRequest\n\t5,  // 13: jaeger.api_v2.QueryService.FindTraces:input_type -> jaeger.api_v2.FindTracesRequest\n\t6,  // 14: jaeger.api_v2.QueryService.GetServices:input_type -> jaeger.api_v2.GetServicesRequest\n\t8,  // 15: jaeger.api_v2.QueryService.GetOperations:input_type -> jaeger.api_v2.GetOperationsRequest\n\t11, // 16: jaeger.api_v2.QueryService.GetDependencies:input_type -> jaeger.api_v2.GetDependenciesRequest\n\t1,  // 17: jaeger.api_v2.QueryService.GetTrace:output_type -> jaeger.api_v2.SpansResponseChunk\n\t3,  // 18: jaeger.api_v2.QueryService.ArchiveTrace:output_type -> jaeger.api_v2.ArchiveTraceResponse\n\t1,  // 19: jaeger.api_v2.QueryService.FindTraces:output_type -> jaeger.api_v2.SpansResponseChunk\n\t7,  // 20: jaeger.api_v2.QueryService.GetServices:output_type -> jaeger.api_v2.GetServicesResponse\n\t10, // 21: jaeger.api_v2.QueryService.GetOperations:output_type -> jaeger.api_v2.GetOperationsResponse\n\t12, // 22: jaeger.api_v2.QueryService.GetDependencies:output_type -> jaeger.api_v2.GetDependenciesResponse\n\t17, // [17:23] is the sub-list for method output_type\n\t11, // [11:17] is the sub-list for method input_type\n\t11, // [11:11] is the sub-list for extension type_name\n\t11, // [11:11] is the sub-list for extension extendee\n\t0,  // [0:11] is the sub-list for field type_name\n}\n\nfunc init() { file_jaeger_query_proto_init() }\nfunc file_jaeger_query_proto_init() {\n\tif File_jaeger_query_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_jaeger_query_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*GetTraceRequest); 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_jaeger_query_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*SpansResponseChunk); 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_jaeger_query_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ArchiveTraceRequest); 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_jaeger_query_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ArchiveTraceResponse); 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_jaeger_query_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*TraceQueryParameters); 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_jaeger_query_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*FindTracesRequest); 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_jaeger_query_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*GetServicesRequest); 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_jaeger_query_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*GetServicesResponse); 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_jaeger_query_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*GetOperationsRequest); 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_jaeger_query_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Operation); 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_jaeger_query_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*GetOperationsResponse); 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_jaeger_query_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*GetDependenciesRequest); 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_jaeger_query_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*GetDependenciesResponse); 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_jaeger_query_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   14,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_jaeger_query_proto_goTypes,\n\t\tDependencyIndexes: file_jaeger_query_proto_depIdxs,\n\t\tMessageInfos:      file_jaeger_query_proto_msgTypes,\n\t}.Build()\n\tFile_jaeger_query_proto = out.File\n\tfile_jaeger_query_proto_rawDesc = nil\n\tfile_jaeger_query_proto_goTypes = nil\n\tfile_jaeger_query_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "chapter/11/ops/proto/jaeger/query.proto",
    "content": "// Copyright (c) 2019 The Jaeger Authors.\n// Copyright (c) 2018 Uber Technologies, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax=\"proto3\";\n\npackage jaeger.api_v2;\n\nimport \"jaeger/model/model.proto\";\nimport \"gogoproto/gogo.proto\";\nimport \"google/api/annotations.proto\";\nimport \"google/protobuf/timestamp.proto\";\nimport \"google/protobuf/duration.proto\";\n\noption go_package = \"github.com/PacktPublishing/Go-for-DevOps/chapter/10/ops/proto/jaeger\";\noption java_package = \"io.jaegertracing.api_v2\";\n\n// Enable gogoprotobuf extensions (https://github.com/gogo/protobuf/blob/master/extensions.md).\n// Enable custom Marshal method.\noption (gogoproto.marshaler_all) = true;\n// Enable custom Unmarshal method.\noption (gogoproto.unmarshaler_all) = true;\n// Enable custom Size method (Required by Marshal and Unmarshal).\noption (gogoproto.sizer_all) = true;\n\nmessage GetTraceRequest {\n  bytes trace_id = 1 [\n    (gogoproto.nullable) = false,\n    (gogoproto.customtype) = \"github.com/jaegertracing/jaeger/model.TraceID\",\n    (gogoproto.customname) = \"TraceID\"\n  ];\n}\n\nmessage SpansResponseChunk {\n  repeated jaeger.api_v2.Span spans = 1 [\n    (gogoproto.nullable) = false\n  ];\n}\n\nmessage ArchiveTraceRequest {\n  bytes trace_id = 1 [\n    (gogoproto.nullable) = false,\n    (gogoproto.customtype) = \"github.com/jaegertracing/jaeger/model.TraceID\",\n    (gogoproto.customname) = \"TraceID\"\n  ];\n}\n\nmessage ArchiveTraceResponse {\n}\n\nmessage TraceQueryParameters {\n  string service_name = 1;\n  string operation_name = 2;\n  map<string, string> tags = 3;\n  google.protobuf.Timestamp start_time_min = 4 [\n    (gogoproto.stdtime) = true,\n    (gogoproto.nullable) = false\n  ];\n  google.protobuf.Timestamp start_time_max = 5 [\n    (gogoproto.stdtime) = true,\n    (gogoproto.nullable) = false\n  ];\n  google.protobuf.Duration duration_min = 6 [\n    (gogoproto.stdduration) = true,\n    (gogoproto.nullable) = false\n  ];\n  google.protobuf.Duration duration_max = 7 [\n    (gogoproto.stdduration) = true,\n    (gogoproto.nullable) = false\n  ];\n  int32 search_depth = 8;\n}\n\nmessage FindTracesRequest {\n  TraceQueryParameters query = 1;\n}\n\nmessage GetServicesRequest {}\n\nmessage GetServicesResponse {\n  repeated string services = 1;\n}\n\nmessage GetOperationsRequest {\n  string service = 1;\n  string span_kind = 2;\n}\n\nmessage Operation {\n    string name = 1;\n    string span_kind = 2;\n}\n\nmessage GetOperationsResponse {\n  repeated string operationNames = 1; //deprecated\n  repeated Operation operations = 2;\n}\n\nmessage GetDependenciesRequest {\n  google.protobuf.Timestamp start_time = 1 [\n    (gogoproto.stdtime) = true,\n    (gogoproto.nullable) = false\n  ];\n  google.protobuf.Timestamp end_time = 2 [\n    (gogoproto.stdtime) = true,\n    (gogoproto.nullable) = false\n  ];\n}\n\nmessage GetDependenciesResponse {\n  repeated jaeger.api_v2.DependencyLink dependencies = 1 [\n    (gogoproto.nullable) = false\n  ];\n}\n\nservice QueryService {\n    rpc GetTrace(GetTraceRequest) returns (stream SpansResponseChunk) {\n        option (google.api.http) = {\n            get: \"/traces/{trace_id}\"\n        };\n    }\n\n    rpc ArchiveTrace(ArchiveTraceRequest) returns (ArchiveTraceResponse) {\n        option (google.api.http) = {\n            post: \"/archive/{trace_id}\"\n        };\n    }\n\n    rpc FindTraces(FindTracesRequest) returns (stream SpansResponseChunk) {\n        option (google.api.http) = {\n            post: \"/search\"\n            body: \"*\"\n        };\n    }\n\n    rpc GetServices(GetServicesRequest) returns (GetServicesResponse) {\n        option (google.api.http) = {\n            get: \"/services\"\n        };\n    }\n\n    rpc GetOperations(GetOperationsRequest) returns (GetOperationsResponse) {\n        option (google.api.http) = {\n            get: \"/operations\"\n        };\n    }\n\n    rpc GetDependencies(GetDependenciesRequest) returns (GetDependenciesResponse) {\n        option (google.api.http) = {\n            get: \"/dependencies\"\n        };\n    }\n}\n"
  },
  {
    "path": "chapter/11/ops/proto/jaeger/query_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n\npackage jaeger\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// QueryServiceClient is the client API for QueryService 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 QueryServiceClient interface {\n\tGetTrace(ctx context.Context, in *GetTraceRequest, opts ...grpc.CallOption) (QueryService_GetTraceClient, error)\n\tArchiveTrace(ctx context.Context, in *ArchiveTraceRequest, opts ...grpc.CallOption) (*ArchiveTraceResponse, error)\n\tFindTraces(ctx context.Context, in *FindTracesRequest, opts ...grpc.CallOption) (QueryService_FindTracesClient, error)\n\tGetServices(ctx context.Context, in *GetServicesRequest, opts ...grpc.CallOption) (*GetServicesResponse, error)\n\tGetOperations(ctx context.Context, in *GetOperationsRequest, opts ...grpc.CallOption) (*GetOperationsResponse, error)\n\tGetDependencies(ctx context.Context, in *GetDependenciesRequest, opts ...grpc.CallOption) (*GetDependenciesResponse, error)\n}\n\ntype queryServiceClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewQueryServiceClient(cc grpc.ClientConnInterface) QueryServiceClient {\n\treturn &queryServiceClient{cc}\n}\n\nfunc (c *queryServiceClient) GetTrace(ctx context.Context, in *GetTraceRequest, opts ...grpc.CallOption) (QueryService_GetTraceClient, error) {\n\tstream, err := c.cc.NewStream(ctx, &QueryService_ServiceDesc.Streams[0], \"/jaeger.api_v2.QueryService/GetTrace\", opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &queryServiceGetTraceClient{stream}\n\tif err := x.ClientStream.SendMsg(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := x.ClientStream.CloseSend(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn x, nil\n}\n\ntype QueryService_GetTraceClient interface {\n\tRecv() (*SpansResponseChunk, error)\n\tgrpc.ClientStream\n}\n\ntype queryServiceGetTraceClient struct {\n\tgrpc.ClientStream\n}\n\nfunc (x *queryServiceGetTraceClient) Recv() (*SpansResponseChunk, error) {\n\tm := new(SpansResponseChunk)\n\tif err := x.ClientStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\nfunc (c *queryServiceClient) ArchiveTrace(ctx context.Context, in *ArchiveTraceRequest, opts ...grpc.CallOption) (*ArchiveTraceResponse, error) {\n\tout := new(ArchiveTraceResponse)\n\terr := c.cc.Invoke(ctx, \"/jaeger.api_v2.QueryService/ArchiveTrace\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *queryServiceClient) FindTraces(ctx context.Context, in *FindTracesRequest, opts ...grpc.CallOption) (QueryService_FindTracesClient, error) {\n\tstream, err := c.cc.NewStream(ctx, &QueryService_ServiceDesc.Streams[1], \"/jaeger.api_v2.QueryService/FindTraces\", opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &queryServiceFindTracesClient{stream}\n\tif err := x.ClientStream.SendMsg(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := x.ClientStream.CloseSend(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn x, nil\n}\n\ntype QueryService_FindTracesClient interface {\n\tRecv() (*SpansResponseChunk, error)\n\tgrpc.ClientStream\n}\n\ntype queryServiceFindTracesClient struct {\n\tgrpc.ClientStream\n}\n\nfunc (x *queryServiceFindTracesClient) Recv() (*SpansResponseChunk, error) {\n\tm := new(SpansResponseChunk)\n\tif err := x.ClientStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\nfunc (c *queryServiceClient) GetServices(ctx context.Context, in *GetServicesRequest, opts ...grpc.CallOption) (*GetServicesResponse, error) {\n\tout := new(GetServicesResponse)\n\terr := c.cc.Invoke(ctx, \"/jaeger.api_v2.QueryService/GetServices\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *queryServiceClient) GetOperations(ctx context.Context, in *GetOperationsRequest, opts ...grpc.CallOption) (*GetOperationsResponse, error) {\n\tout := new(GetOperationsResponse)\n\terr := c.cc.Invoke(ctx, \"/jaeger.api_v2.QueryService/GetOperations\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *queryServiceClient) GetDependencies(ctx context.Context, in *GetDependenciesRequest, opts ...grpc.CallOption) (*GetDependenciesResponse, error) {\n\tout := new(GetDependenciesResponse)\n\terr := c.cc.Invoke(ctx, \"/jaeger.api_v2.QueryService/GetDependencies\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// QueryServiceServer is the server API for QueryService service.\n// All implementations must embed UnimplementedQueryServiceServer\n// for forward compatibility\ntype QueryServiceServer interface {\n\tGetTrace(*GetTraceRequest, QueryService_GetTraceServer) error\n\tArchiveTrace(context.Context, *ArchiveTraceRequest) (*ArchiveTraceResponse, error)\n\tFindTraces(*FindTracesRequest, QueryService_FindTracesServer) error\n\tGetServices(context.Context, *GetServicesRequest) (*GetServicesResponse, error)\n\tGetOperations(context.Context, *GetOperationsRequest) (*GetOperationsResponse, error)\n\tGetDependencies(context.Context, *GetDependenciesRequest) (*GetDependenciesResponse, error)\n\tmustEmbedUnimplementedQueryServiceServer()\n}\n\n// UnimplementedQueryServiceServer must be embedded to have forward compatible implementations.\ntype UnimplementedQueryServiceServer struct {\n}\n\nfunc (UnimplementedQueryServiceServer) GetTrace(*GetTraceRequest, QueryService_GetTraceServer) error {\n\treturn status.Errorf(codes.Unimplemented, \"method GetTrace not implemented\")\n}\nfunc (UnimplementedQueryServiceServer) ArchiveTrace(context.Context, *ArchiveTraceRequest) (*ArchiveTraceResponse, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ArchiveTrace not implemented\")\n}\nfunc (UnimplementedQueryServiceServer) FindTraces(*FindTracesRequest, QueryService_FindTracesServer) error {\n\treturn status.Errorf(codes.Unimplemented, \"method FindTraces not implemented\")\n}\nfunc (UnimplementedQueryServiceServer) GetServices(context.Context, *GetServicesRequest) (*GetServicesResponse, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method GetServices not implemented\")\n}\nfunc (UnimplementedQueryServiceServer) GetOperations(context.Context, *GetOperationsRequest) (*GetOperationsResponse, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method GetOperations not implemented\")\n}\nfunc (UnimplementedQueryServiceServer) GetDependencies(context.Context, *GetDependenciesRequest) (*GetDependenciesResponse, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method GetDependencies not implemented\")\n}\nfunc (UnimplementedQueryServiceServer) mustEmbedUnimplementedQueryServiceServer() {}\n\n// UnsafeQueryServiceServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to QueryServiceServer will\n// result in compilation errors.\ntype UnsafeQueryServiceServer interface {\n\tmustEmbedUnimplementedQueryServiceServer()\n}\n\nfunc RegisterQueryServiceServer(s grpc.ServiceRegistrar, srv QueryServiceServer) {\n\ts.RegisterService(&QueryService_ServiceDesc, srv)\n}\n\nfunc _QueryService_GetTrace_Handler(srv interface{}, stream grpc.ServerStream) error {\n\tm := new(GetTraceRequest)\n\tif err := stream.RecvMsg(m); err != nil {\n\t\treturn err\n\t}\n\treturn srv.(QueryServiceServer).GetTrace(m, &queryServiceGetTraceServer{stream})\n}\n\ntype QueryService_GetTraceServer interface {\n\tSend(*SpansResponseChunk) error\n\tgrpc.ServerStream\n}\n\ntype queryServiceGetTraceServer struct {\n\tgrpc.ServerStream\n}\n\nfunc (x *queryServiceGetTraceServer) Send(m *SpansResponseChunk) error {\n\treturn x.ServerStream.SendMsg(m)\n}\n\nfunc _QueryService_ArchiveTrace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ArchiveTraceRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(QueryServiceServer).ArchiveTrace(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/jaeger.api_v2.QueryService/ArchiveTrace\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(QueryServiceServer).ArchiveTrace(ctx, req.(*ArchiveTraceRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _QueryService_FindTraces_Handler(srv interface{}, stream grpc.ServerStream) error {\n\tm := new(FindTracesRequest)\n\tif err := stream.RecvMsg(m); err != nil {\n\t\treturn err\n\t}\n\treturn srv.(QueryServiceServer).FindTraces(m, &queryServiceFindTracesServer{stream})\n}\n\ntype QueryService_FindTracesServer interface {\n\tSend(*SpansResponseChunk) error\n\tgrpc.ServerStream\n}\n\ntype queryServiceFindTracesServer struct {\n\tgrpc.ServerStream\n}\n\nfunc (x *queryServiceFindTracesServer) Send(m *SpansResponseChunk) error {\n\treturn x.ServerStream.SendMsg(m)\n}\n\nfunc _QueryService_GetServices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(GetServicesRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(QueryServiceServer).GetServices(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/jaeger.api_v2.QueryService/GetServices\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(QueryServiceServer).GetServices(ctx, req.(*GetServicesRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _QueryService_GetOperations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(GetOperationsRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(QueryServiceServer).GetOperations(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/jaeger.api_v2.QueryService/GetOperations\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(QueryServiceServer).GetOperations(ctx, req.(*GetOperationsRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _QueryService_GetDependencies_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(GetDependenciesRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(QueryServiceServer).GetDependencies(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/jaeger.api_v2.QueryService/GetDependencies\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(QueryServiceServer).GetDependencies(ctx, req.(*GetDependenciesRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// QueryService_ServiceDesc is the grpc.ServiceDesc for QueryService 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 QueryService_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"jaeger.api_v2.QueryService\",\n\tHandlerType: (*QueryServiceServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"ArchiveTrace\",\n\t\t\tHandler:    _QueryService_ArchiveTrace_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"GetServices\",\n\t\t\tHandler:    _QueryService_GetServices_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"GetOperations\",\n\t\t\tHandler:    _QueryService_GetOperations_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"GetDependencies\",\n\t\t\tHandler:    _QueryService_GetDependencies_Handler,\n\t\t},\n\t},\n\tStreams: []grpc.StreamDesc{\n\t\t{\n\t\t\tStreamName:    \"GetTrace\",\n\t\t\tHandler:       _QueryService_GetTrace_Handler,\n\t\t\tServerStreams: true,\n\t\t},\n\t\t{\n\t\t\tStreamName:    \"FindTraces\",\n\t\t\tHandler:       _QueryService_FindTraces_Handler,\n\t\t\tServerStreams: true,\n\t\t},\n\t},\n\tMetadata: \"jaeger/query.proto\",\n}\n"
  },
  {
    "path": "chapter/11/ops/proto/jaeger/sampling.pb.go",
    "content": "// Copyright (c) 2019 The Jaeger Authors.\n// Copyright (c) 2018 Uber Technologies, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.25.0-devel\n// \tprotoc        v3.18.0\n// source: jaeger/sampling.proto\n\npackage jaeger\n\nimport (\n\t_ \"github.com/gogo/protobuf/gogoproto\"\n\t_ \"google.golang.org/genproto/googleapis/api/annotations\"\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\n// See description of the SamplingStrategyResponse.strategyType field.\ntype SamplingStrategyType int32\n\nconst (\n\tSamplingStrategyType_PROBABILISTIC SamplingStrategyType = 0\n\tSamplingStrategyType_RATE_LIMITING SamplingStrategyType = 1\n)\n\n// Enum value maps for SamplingStrategyType.\nvar (\n\tSamplingStrategyType_name = map[int32]string{\n\t\t0: \"PROBABILISTIC\",\n\t\t1: \"RATE_LIMITING\",\n\t}\n\tSamplingStrategyType_value = map[string]int32{\n\t\t\"PROBABILISTIC\": 0,\n\t\t\"RATE_LIMITING\": 1,\n\t}\n)\n\nfunc (x SamplingStrategyType) Enum() *SamplingStrategyType {\n\tp := new(SamplingStrategyType)\n\t*p = x\n\treturn p\n}\n\nfunc (x SamplingStrategyType) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (SamplingStrategyType) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_jaeger_sampling_proto_enumTypes[0].Descriptor()\n}\n\nfunc (SamplingStrategyType) Type() protoreflect.EnumType {\n\treturn &file_jaeger_sampling_proto_enumTypes[0]\n}\n\nfunc (x SamplingStrategyType) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use SamplingStrategyType.Descriptor instead.\nfunc (SamplingStrategyType) EnumDescriptor() ([]byte, []int) {\n\treturn file_jaeger_sampling_proto_rawDescGZIP(), []int{0}\n}\n\n// ProbabilisticSamplingStrategy samples traces with a fixed probability.\ntype ProbabilisticSamplingStrategy struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// samplingRate is the sampling probability in the range [0.0, 1.0].\n\tSamplingRate float64 `protobuf:\"fixed64,1,opt,name=samplingRate,proto3\" json:\"samplingRate,omitempty\"`\n}\n\nfunc (x *ProbabilisticSamplingStrategy) Reset() {\n\t*x = ProbabilisticSamplingStrategy{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_sampling_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ProbabilisticSamplingStrategy) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ProbabilisticSamplingStrategy) ProtoMessage() {}\n\nfunc (x *ProbabilisticSamplingStrategy) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_sampling_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 ProbabilisticSamplingStrategy.ProtoReflect.Descriptor instead.\nfunc (*ProbabilisticSamplingStrategy) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_sampling_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *ProbabilisticSamplingStrategy) GetSamplingRate() float64 {\n\tif x != nil {\n\t\treturn x.SamplingRate\n\t}\n\treturn 0\n}\n\n// RateLimitingSamplingStrategy samples a fixed number of traces per time interval.\n// The typical implementations use the leaky bucket algorithm.\ntype RateLimitingSamplingStrategy struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// TODO this field type should be changed to double, to support rates like 1 per minute.\n\tMaxTracesPerSecond int32 `protobuf:\"varint,1,opt,name=maxTracesPerSecond,proto3\" json:\"maxTracesPerSecond,omitempty\"`\n}\n\nfunc (x *RateLimitingSamplingStrategy) Reset() {\n\t*x = RateLimitingSamplingStrategy{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_sampling_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *RateLimitingSamplingStrategy) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*RateLimitingSamplingStrategy) ProtoMessage() {}\n\nfunc (x *RateLimitingSamplingStrategy) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_sampling_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 RateLimitingSamplingStrategy.ProtoReflect.Descriptor instead.\nfunc (*RateLimitingSamplingStrategy) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_sampling_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *RateLimitingSamplingStrategy) GetMaxTracesPerSecond() int32 {\n\tif x != nil {\n\t\treturn x.MaxTracesPerSecond\n\t}\n\treturn 0\n}\n\n// OperationSamplingStrategy is a sampling strategy for a given operation\n// (aka endpoint, span name). Only probabilistic sampling is currently supported.\ntype OperationSamplingStrategy struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tOperation             string                         `protobuf:\"bytes,1,opt,name=operation,proto3\" json:\"operation,omitempty\"`\n\tProbabilisticSampling *ProbabilisticSamplingStrategy `protobuf:\"bytes,2,opt,name=probabilisticSampling,proto3\" json:\"probabilisticSampling,omitempty\"`\n}\n\nfunc (x *OperationSamplingStrategy) Reset() {\n\t*x = OperationSamplingStrategy{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_sampling_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *OperationSamplingStrategy) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*OperationSamplingStrategy) ProtoMessage() {}\n\nfunc (x *OperationSamplingStrategy) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_sampling_proto_msgTypes[2]\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 OperationSamplingStrategy.ProtoReflect.Descriptor instead.\nfunc (*OperationSamplingStrategy) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_sampling_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *OperationSamplingStrategy) GetOperation() string {\n\tif x != nil {\n\t\treturn x.Operation\n\t}\n\treturn \"\"\n}\n\nfunc (x *OperationSamplingStrategy) GetProbabilisticSampling() *ProbabilisticSamplingStrategy {\n\tif x != nil {\n\t\treturn x.ProbabilisticSampling\n\t}\n\treturn nil\n}\n\n// PerOperationSamplingStrategies is a combination of strategies for different endpoints\n// as well as some service-wide defaults. It is particularly useful for services whose\n// endpoints receive vastly different traffic, so that any single rate of sampling would\n// result in either too much data for some endpoints or almost no data for other endpoints.\ntype PerOperationSamplingStrategies struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// defaultSamplingProbability is the sampling probability for spans that do not match\n\t// any of the perOperationStrategies.\n\tDefaultSamplingProbability float64 `protobuf:\"fixed64,1,opt,name=defaultSamplingProbability,proto3\" json:\"defaultSamplingProbability,omitempty\"`\n\t// defaultLowerBoundTracesPerSecond defines a lower-bound rate limit used to ensure that\n\t// there is some minimal amount of traces sampled for an endpoint that might otherwise\n\t// be never sampled via probabilistic strategies. The limit is local to a service instance,\n\t// so if a service is deployed with many (N) instances, the effective minimum rate of sampling\n\t// will be N times higher. This setting applies to ALL operations, whether or not they match\n\t// one of the perOperationStrategies.\n\tDefaultLowerBoundTracesPerSecond float64 `protobuf:\"fixed64,2,opt,name=defaultLowerBoundTracesPerSecond,proto3\" json:\"defaultLowerBoundTracesPerSecond,omitempty\"`\n\t// perOperationStrategies describes sampling strategiesf for individual operations within\n\t// a given service.\n\tPerOperationStrategies []*OperationSamplingStrategy `protobuf:\"bytes,3,rep,name=perOperationStrategies,proto3\" json:\"perOperationStrategies,omitempty\"`\n\t// defaultUpperBoundTracesPerSecond defines an upper bound rate limit.\n\t// However, almost no Jaeger SDKs support this parameter.\n\tDefaultUpperBoundTracesPerSecond float64 `protobuf:\"fixed64,4,opt,name=defaultUpperBoundTracesPerSecond,proto3\" json:\"defaultUpperBoundTracesPerSecond,omitempty\"`\n}\n\nfunc (x *PerOperationSamplingStrategies) Reset() {\n\t*x = PerOperationSamplingStrategies{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_sampling_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *PerOperationSamplingStrategies) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*PerOperationSamplingStrategies) ProtoMessage() {}\n\nfunc (x *PerOperationSamplingStrategies) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_sampling_proto_msgTypes[3]\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 PerOperationSamplingStrategies.ProtoReflect.Descriptor instead.\nfunc (*PerOperationSamplingStrategies) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_sampling_proto_rawDescGZIP(), []int{3}\n}\n\nfunc (x *PerOperationSamplingStrategies) GetDefaultSamplingProbability() float64 {\n\tif x != nil {\n\t\treturn x.DefaultSamplingProbability\n\t}\n\treturn 0\n}\n\nfunc (x *PerOperationSamplingStrategies) GetDefaultLowerBoundTracesPerSecond() float64 {\n\tif x != nil {\n\t\treturn x.DefaultLowerBoundTracesPerSecond\n\t}\n\treturn 0\n}\n\nfunc (x *PerOperationSamplingStrategies) GetPerOperationStrategies() []*OperationSamplingStrategy {\n\tif x != nil {\n\t\treturn x.PerOperationStrategies\n\t}\n\treturn nil\n}\n\nfunc (x *PerOperationSamplingStrategies) GetDefaultUpperBoundTracesPerSecond() float64 {\n\tif x != nil {\n\t\treturn x.DefaultUpperBoundTracesPerSecond\n\t}\n\treturn 0\n}\n\n// SamplingStrategyResponse contains an overall sampling strategy for a given service.\n// This type should be treated as a union where only one of the strategy field is present.\ntype SamplingStrategyResponse struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Legacy field that was meant to indicate which one of the strategy fields\n\t// below is present. This enum was not extended when per-operation strategy\n\t// was introduced, because extending enum has backwards compatiblity issues.\n\t// The recommended approach for consumers is to ignore this field and instead\n\t// checks the other fields being not null (starting with operationSampling).\n\t// For producers, it is recommended to set this field correctly for probabilistic\n\t// and rate-limiting strategies, but if per-operation strategy is returned,\n\t// the enum can be set to 0 (probabilistic).\n\tStrategyType          SamplingStrategyType            `protobuf:\"varint,1,opt,name=strategyType,proto3,enum=jaeger.api_v2.SamplingStrategyType\" json:\"strategyType,omitempty\"`\n\tProbabilisticSampling *ProbabilisticSamplingStrategy  `protobuf:\"bytes,2,opt,name=probabilisticSampling,proto3\" json:\"probabilisticSampling,omitempty\"`\n\tRateLimitingSampling  *RateLimitingSamplingStrategy   `protobuf:\"bytes,3,opt,name=rateLimitingSampling,proto3\" json:\"rateLimitingSampling,omitempty\"`\n\tOperationSampling     *PerOperationSamplingStrategies `protobuf:\"bytes,4,opt,name=operationSampling,proto3\" json:\"operationSampling,omitempty\"`\n}\n\nfunc (x *SamplingStrategyResponse) Reset() {\n\t*x = SamplingStrategyResponse{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_sampling_proto_msgTypes[4]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *SamplingStrategyResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*SamplingStrategyResponse) ProtoMessage() {}\n\nfunc (x *SamplingStrategyResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_sampling_proto_msgTypes[4]\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 SamplingStrategyResponse.ProtoReflect.Descriptor instead.\nfunc (*SamplingStrategyResponse) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_sampling_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (x *SamplingStrategyResponse) GetStrategyType() SamplingStrategyType {\n\tif x != nil {\n\t\treturn x.StrategyType\n\t}\n\treturn SamplingStrategyType_PROBABILISTIC\n}\n\nfunc (x *SamplingStrategyResponse) GetProbabilisticSampling() *ProbabilisticSamplingStrategy {\n\tif x != nil {\n\t\treturn x.ProbabilisticSampling\n\t}\n\treturn nil\n}\n\nfunc (x *SamplingStrategyResponse) GetRateLimitingSampling() *RateLimitingSamplingStrategy {\n\tif x != nil {\n\t\treturn x.RateLimitingSampling\n\t}\n\treturn nil\n}\n\nfunc (x *SamplingStrategyResponse) GetOperationSampling() *PerOperationSamplingStrategies {\n\tif x != nil {\n\t\treturn x.OperationSampling\n\t}\n\treturn nil\n}\n\n// SamplingStrategyParameters defines request parameters for remote sampler.\ntype SamplingStrategyParameters struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// serviceName is a required argument.\n\tServiceName string `protobuf:\"bytes,1,opt,name=serviceName,proto3\" json:\"serviceName,omitempty\"`\n}\n\nfunc (x *SamplingStrategyParameters) Reset() {\n\t*x = SamplingStrategyParameters{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_jaeger_sampling_proto_msgTypes[5]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *SamplingStrategyParameters) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*SamplingStrategyParameters) ProtoMessage() {}\n\nfunc (x *SamplingStrategyParameters) ProtoReflect() protoreflect.Message {\n\tmi := &file_jaeger_sampling_proto_msgTypes[5]\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 SamplingStrategyParameters.ProtoReflect.Descriptor instead.\nfunc (*SamplingStrategyParameters) Descriptor() ([]byte, []int) {\n\treturn file_jaeger_sampling_proto_rawDescGZIP(), []int{5}\n}\n\nfunc (x *SamplingStrategyParameters) GetServiceName() string {\n\tif x != nil {\n\t\treturn x.ServiceName\n\t}\n\treturn \"\"\n}\n\nvar File_jaeger_sampling_proto protoreflect.FileDescriptor\n\nvar file_jaeger_sampling_proto_rawDesc = []byte{\n\t0x0a, 0x15, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e,\n\t0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e,\n\t0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x1a, 0x14, 0x67, 0x6f, 0x67, 0x6f, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x2f, 0x67, 0x6f, 0x67, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f,\n\t0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74,\n\t0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x43, 0x0a, 0x1d, 0x50, 0x72,\n\t0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x73, 0x74, 0x69, 0x63, 0x53, 0x61, 0x6d, 0x70, 0x6c,\n\t0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x73,\n\t0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x52, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x01, 0x52, 0x0c, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x52, 0x61, 0x74, 0x65, 0x22,\n\t0x4e, 0x0a, 0x1c, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x53,\n\t0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12,\n\t0x2e, 0x0a, 0x12, 0x6d, 0x61, 0x78, 0x54, 0x72, 0x61, 0x63, 0x65, 0x73, 0x50, 0x65, 0x72, 0x53,\n\t0x65, 0x63, 0x6f, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x6d, 0x61, 0x78,\n\t0x54, 0x72, 0x61, 0x63, 0x65, 0x73, 0x50, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x22,\n\t0x9d, 0x01, 0x0a, 0x19, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x61, 0x6d,\n\t0x70, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x1c, 0x0a,\n\t0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,\n\t0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x62, 0x0a, 0x15, 0x70,\n\t0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x73, 0x74, 0x69, 0x63, 0x53, 0x61, 0x6d, 0x70,\n\t0x6c, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x6a, 0x61, 0x65,\n\t0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x6f, 0x62, 0x61,\n\t0x62, 0x69, 0x6c, 0x69, 0x73, 0x74, 0x69, 0x63, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67,\n\t0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x15, 0x70, 0x72, 0x6f, 0x62, 0x61, 0x62,\n\t0x69, 0x6c, 0x69, 0x73, 0x74, 0x69, 0x63, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x22,\n\t0xda, 0x02, 0x0a, 0x1e, 0x50, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,\n\t0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x69,\n\t0x65, 0x73, 0x12, 0x3e, 0x0a, 0x1a, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x61, 0x6d,\n\t0x70, 0x6c, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x1a, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53,\n\t0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69,\n\t0x74, 0x79, 0x12, 0x4a, 0x0a, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4c, 0x6f, 0x77,\n\t0x65, 0x72, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x63, 0x65, 0x73, 0x50, 0x65, 0x72,\n\t0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x20, 0x64, 0x65,\n\t0x66, 0x61, 0x75, 0x6c, 0x74, 0x4c, 0x6f, 0x77, 0x65, 0x72, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x54,\n\t0x72, 0x61, 0x63, 0x65, 0x73, 0x50, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x12, 0x60,\n\t0x0a, 0x16, 0x70, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74,\n\t0x72, 0x61, 0x74, 0x65, 0x67, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28,\n\t0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e, 0x4f,\n\t0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67,\n\t0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x16, 0x70, 0x65, 0x72, 0x4f, 0x70, 0x65,\n\t0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x69, 0x65, 0x73,\n\t0x12, 0x4a, 0x0a, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x55, 0x70, 0x70, 0x65, 0x72,\n\t0x42, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x63, 0x65, 0x73, 0x50, 0x65, 0x72, 0x53, 0x65,\n\t0x63, 0x6f, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x20, 0x64, 0x65, 0x66, 0x61,\n\t0x75, 0x6c, 0x74, 0x55, 0x70, 0x70, 0x65, 0x72, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, 0x61,\n\t0x63, 0x65, 0x73, 0x50, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x22, 0x85, 0x03, 0x0a,\n\t0x18, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67,\n\t0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x73, 0x74, 0x72,\n\t0x61, 0x74, 0x65, 0x67, 0x79, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,\n\t0x23, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e,\n\t0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,\n\t0x54, 0x79, 0x70, 0x65, 0x52, 0x0c, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x54, 0x79,\n\t0x70, 0x65, 0x12, 0x62, 0x0a, 0x15, 0x70, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x73,\n\t0x74, 0x69, 0x63, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28,\n\t0x0b, 0x32, 0x2c, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76,\n\t0x32, 0x2e, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x73, 0x74, 0x69, 0x63, 0x53,\n\t0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52,\n\t0x15, 0x70, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x73, 0x74, 0x69, 0x63, 0x53, 0x61,\n\t0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x12, 0x5f, 0x0a, 0x14, 0x72, 0x61, 0x74, 0x65, 0x4c, 0x69,\n\t0x6d, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x03,\n\t0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70,\n\t0x69, 0x5f, 0x76, 0x32, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x6e,\n\t0x67, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67,\n\t0x79, 0x52, 0x14, 0x72, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x53,\n\t0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x12, 0x5b, 0x0a, 0x11, 0x6f, 0x70, 0x65, 0x72, 0x61,\n\t0x74, 0x69, 0x6f, 0x6e, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20, 0x01,\n\t0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f,\n\t0x76, 0x32, 0x2e, 0x50, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53,\n\t0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x69, 0x65,\n\t0x73, 0x52, 0x11, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x61, 0x6d, 0x70,\n\t0x6c, 0x69, 0x6e, 0x67, 0x22, 0x3e, 0x0a, 0x1a, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67,\n\t0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65,\n\t0x72, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d,\n\t0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,\n\t0x4e, 0x61, 0x6d, 0x65, 0x2a, 0x3c, 0x0a, 0x14, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67,\n\t0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x11, 0x0a, 0x0d,\n\t0x50, 0x52, 0x4f, 0x42, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x53, 0x54, 0x49, 0x43, 0x10, 0x00, 0x12,\n\t0x11, 0x0a, 0x0d, 0x52, 0x41, 0x54, 0x45, 0x5f, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x49, 0x4e, 0x47,\n\t0x10, 0x01, 0x32, 0xa2, 0x01, 0x0a, 0x0f, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x4d,\n\t0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x8e, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x61,\n\t0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x29,\n\t0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e, 0x53,\n\t0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x50,\n\t0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x1a, 0x27, 0x2e, 0x6a, 0x61, 0x65, 0x67,\n\t0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69,\n\t0x6e, 0x67, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,\n\t0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, 0x2f, 0x61, 0x70, 0x69,\n\t0x2f, 0x76, 0x32, 0x2f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x61,\n\t0x74, 0x65, 0x67, 0x79, 0x3a, 0x01, 0x2a, 0x42, 0x6b, 0x0a, 0x17, 0x69, 0x6f, 0x2e, 0x6a, 0x61,\n\t0x65, 0x67, 0x65, 0x72, 0x74, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x2e, 0x61, 0x70, 0x69, 0x5f,\n\t0x76, 0x32, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50,\n\t0x61, 0x63, 0x6b, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x2f, 0x47,\n\t0x6f, 0x2d, 0x66, 0x6f, 0x72, 0x2d, 0x44, 0x65, 0x76, 0x4f, 0x70, 0x73, 0x2f, 0x63, 0x68, 0x61,\n\t0x70, 0x74, 0x65, 0x72, 0x2f, 0x31, 0x30, 0x2f, 0x6f, 0x70, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x2f, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0xc8, 0xe2, 0x1e, 0x01, 0xd0, 0xe2, 0x1e, 0x01,\n\t0xe0, 0xe2, 0x1e, 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_jaeger_sampling_proto_rawDescOnce sync.Once\n\tfile_jaeger_sampling_proto_rawDescData = file_jaeger_sampling_proto_rawDesc\n)\n\nfunc file_jaeger_sampling_proto_rawDescGZIP() []byte {\n\tfile_jaeger_sampling_proto_rawDescOnce.Do(func() {\n\t\tfile_jaeger_sampling_proto_rawDescData = protoimpl.X.CompressGZIP(file_jaeger_sampling_proto_rawDescData)\n\t})\n\treturn file_jaeger_sampling_proto_rawDescData\n}\n\nvar file_jaeger_sampling_proto_enumTypes = make([]protoimpl.EnumInfo, 1)\nvar file_jaeger_sampling_proto_msgTypes = make([]protoimpl.MessageInfo, 6)\nvar file_jaeger_sampling_proto_goTypes = []interface{}{\n\t(SamplingStrategyType)(0),              // 0: jaeger.api_v2.SamplingStrategyType\n\t(*ProbabilisticSamplingStrategy)(nil),  // 1: jaeger.api_v2.ProbabilisticSamplingStrategy\n\t(*RateLimitingSamplingStrategy)(nil),   // 2: jaeger.api_v2.RateLimitingSamplingStrategy\n\t(*OperationSamplingStrategy)(nil),      // 3: jaeger.api_v2.OperationSamplingStrategy\n\t(*PerOperationSamplingStrategies)(nil), // 4: jaeger.api_v2.PerOperationSamplingStrategies\n\t(*SamplingStrategyResponse)(nil),       // 5: jaeger.api_v2.SamplingStrategyResponse\n\t(*SamplingStrategyParameters)(nil),     // 6: jaeger.api_v2.SamplingStrategyParameters\n}\nvar file_jaeger_sampling_proto_depIdxs = []int32{\n\t1, // 0: jaeger.api_v2.OperationSamplingStrategy.probabilisticSampling:type_name -> jaeger.api_v2.ProbabilisticSamplingStrategy\n\t3, // 1: jaeger.api_v2.PerOperationSamplingStrategies.perOperationStrategies:type_name -> jaeger.api_v2.OperationSamplingStrategy\n\t0, // 2: jaeger.api_v2.SamplingStrategyResponse.strategyType:type_name -> jaeger.api_v2.SamplingStrategyType\n\t1, // 3: jaeger.api_v2.SamplingStrategyResponse.probabilisticSampling:type_name -> jaeger.api_v2.ProbabilisticSamplingStrategy\n\t2, // 4: jaeger.api_v2.SamplingStrategyResponse.rateLimitingSampling:type_name -> jaeger.api_v2.RateLimitingSamplingStrategy\n\t4, // 5: jaeger.api_v2.SamplingStrategyResponse.operationSampling:type_name -> jaeger.api_v2.PerOperationSamplingStrategies\n\t6, // 6: jaeger.api_v2.SamplingManager.GetSamplingStrategy:input_type -> jaeger.api_v2.SamplingStrategyParameters\n\t5, // 7: jaeger.api_v2.SamplingManager.GetSamplingStrategy:output_type -> jaeger.api_v2.SamplingStrategyResponse\n\t7, // [7:8] is the sub-list for method output_type\n\t6, // [6:7] is the sub-list for method input_type\n\t6, // [6:6] is the sub-list for extension type_name\n\t6, // [6:6] is the sub-list for extension extendee\n\t0, // [0:6] is the sub-list for field type_name\n}\n\nfunc init() { file_jaeger_sampling_proto_init() }\nfunc file_jaeger_sampling_proto_init() {\n\tif File_jaeger_sampling_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_jaeger_sampling_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ProbabilisticSamplingStrategy); 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_jaeger_sampling_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*RateLimitingSamplingStrategy); 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_jaeger_sampling_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*OperationSamplingStrategy); 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_jaeger_sampling_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*PerOperationSamplingStrategies); 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_jaeger_sampling_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*SamplingStrategyResponse); 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_jaeger_sampling_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*SamplingStrategyParameters); 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_jaeger_sampling_proto_rawDesc,\n\t\t\tNumEnums:      1,\n\t\t\tNumMessages:   6,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_jaeger_sampling_proto_goTypes,\n\t\tDependencyIndexes: file_jaeger_sampling_proto_depIdxs,\n\t\tEnumInfos:         file_jaeger_sampling_proto_enumTypes,\n\t\tMessageInfos:      file_jaeger_sampling_proto_msgTypes,\n\t}.Build()\n\tFile_jaeger_sampling_proto = out.File\n\tfile_jaeger_sampling_proto_rawDesc = nil\n\tfile_jaeger_sampling_proto_goTypes = nil\n\tfile_jaeger_sampling_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "chapter/11/ops/proto/jaeger/sampling.proto",
    "content": "// Copyright (c) 2019 The Jaeger Authors.\n// Copyright (c) 2018 Uber Technologies, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax=\"proto3\";\n\npackage jaeger.api_v2;\n\nimport \"gogoproto/gogo.proto\";\nimport \"google/api/annotations.proto\";\n\noption go_package = \"github.com/PacktPublishing/Go-for-DevOps/chapter/10/ops/proto/jaeger\";\noption java_package = \"io.jaegertracing.api_v2\";\n\n// Enable gogoprotobuf extensions (https://github.com/gogo/protobuf/blob/master/extensions.md).\n// Enable custom Marshal method.\noption (gogoproto.marshaler_all) = true;\n// Enable custom Unmarshal method.\noption (gogoproto.unmarshaler_all) = true;\n// Enable custom Size method (Required by Marshal and Unmarshal).\noption (gogoproto.sizer_all) = true;\n\n// See description of the SamplingStrategyResponse.strategyType field.\nenum SamplingStrategyType {\n  PROBABILISTIC = 0;\n  RATE_LIMITING = 1;\n};\n\n// ProbabilisticSamplingStrategy samples traces with a fixed probability.\nmessage ProbabilisticSamplingStrategy {\n  // samplingRate is the sampling probability in the range [0.0, 1.0].\n  double samplingRate = 1;\n}\n\n// RateLimitingSamplingStrategy samples a fixed number of traces per time interval.\n// The typical implementations use the leaky bucket algorithm.\nmessage RateLimitingSamplingStrategy {\n  // TODO this field type should be changed to double, to support rates like 1 per minute.\n  int32 maxTracesPerSecond = 1;\n}\n\n// OperationSamplingStrategy is a sampling strategy for a given operation\n// (aka endpoint, span name). Only probabilistic sampling is currently supported.\nmessage OperationSamplingStrategy {\n  string operation = 1;\n  ProbabilisticSamplingStrategy probabilisticSampling = 2;\n}\n\n// PerOperationSamplingStrategies is a combination of strategies for different endpoints\n// as well as some service-wide defaults. It is particularly useful for services whose\n// endpoints receive vastly different traffic, so that any single rate of sampling would\n// result in either too much data for some endpoints or almost no data for other endpoints.\nmessage PerOperationSamplingStrategies {\n  // defaultSamplingProbability is the sampling probability for spans that do not match\n  // any of the perOperationStrategies.\n  double defaultSamplingProbability = 1;\n\n  // defaultLowerBoundTracesPerSecond defines a lower-bound rate limit used to ensure that\n  // there is some minimal amount of traces sampled for an endpoint that might otherwise\n  // be never sampled via probabilistic strategies. The limit is local to a service instance,\n  // so if a service is deployed with many (N) instances, the effective minimum rate of sampling\n  // will be N times higher. This setting applies to ALL operations, whether or not they match\n  // one of the perOperationStrategies.\n  double defaultLowerBoundTracesPerSecond = 2;\n\n  // perOperationStrategies describes sampling strategiesf for individual operations within\n  // a given service.\n  repeated OperationSamplingStrategy perOperationStrategies = 3;\n\n  // defaultUpperBoundTracesPerSecond defines an upper bound rate limit.\n  // However, almost no Jaeger SDKs support this parameter.\n  double defaultUpperBoundTracesPerSecond = 4;\n}\n\n// SamplingStrategyResponse contains an overall sampling strategy for a given service.\n// This type should be treated as a union where only one of the strategy field is present.\nmessage SamplingStrategyResponse {\n  // Legacy field that was meant to indicate which one of the strategy fields\n  // below is present. This enum was not extended when per-operation strategy\n  // was introduced, because extending enum has backwards compatiblity issues.\n  // The recommended approach for consumers is to ignore this field and instead\n  // checks the other fields being not null (starting with operationSampling).\n  // For producers, it is recommended to set this field correctly for probabilistic\n  // and rate-limiting strategies, but if per-operation strategy is returned,\n  // the enum can be set to 0 (probabilistic).\n  SamplingStrategyType strategyType = 1;\n\n  ProbabilisticSamplingStrategy probabilisticSampling = 2;\n\n  RateLimitingSamplingStrategy rateLimitingSampling = 3;\n\n  PerOperationSamplingStrategies operationSampling = 4;\n}\n\n// SamplingStrategyParameters defines request parameters for remote sampler.\nmessage SamplingStrategyParameters {\n  // serviceName is a required argument.\n  string serviceName = 1;\n}\n\n// SamplingManager defines service for the remote sampler.\nservice SamplingManager {\n  rpc GetSamplingStrategy(SamplingStrategyParameters) returns (SamplingStrategyResponse) {\n    option (google.api.http) = {\n            post: \"/api/v2/samplingStrategy\"\n            body: \"*\"\n        };\n  }\n}\n"
  },
  {
    "path": "chapter/11/ops/proto/jaeger/sampling_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n\npackage jaeger\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// SamplingManagerClient is the client API for SamplingManager 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 SamplingManagerClient interface {\n\tGetSamplingStrategy(ctx context.Context, in *SamplingStrategyParameters, opts ...grpc.CallOption) (*SamplingStrategyResponse, error)\n}\n\ntype samplingManagerClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewSamplingManagerClient(cc grpc.ClientConnInterface) SamplingManagerClient {\n\treturn &samplingManagerClient{cc}\n}\n\nfunc (c *samplingManagerClient) GetSamplingStrategy(ctx context.Context, in *SamplingStrategyParameters, opts ...grpc.CallOption) (*SamplingStrategyResponse, error) {\n\tout := new(SamplingStrategyResponse)\n\terr := c.cc.Invoke(ctx, \"/jaeger.api_v2.SamplingManager/GetSamplingStrategy\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// SamplingManagerServer is the server API for SamplingManager service.\n// All implementations must embed UnimplementedSamplingManagerServer\n// for forward compatibility\ntype SamplingManagerServer interface {\n\tGetSamplingStrategy(context.Context, *SamplingStrategyParameters) (*SamplingStrategyResponse, error)\n\tmustEmbedUnimplementedSamplingManagerServer()\n}\n\n// UnimplementedSamplingManagerServer must be embedded to have forward compatible implementations.\ntype UnimplementedSamplingManagerServer struct {\n}\n\nfunc (UnimplementedSamplingManagerServer) GetSamplingStrategy(context.Context, *SamplingStrategyParameters) (*SamplingStrategyResponse, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method GetSamplingStrategy not implemented\")\n}\nfunc (UnimplementedSamplingManagerServer) mustEmbedUnimplementedSamplingManagerServer() {}\n\n// UnsafeSamplingManagerServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to SamplingManagerServer will\n// result in compilation errors.\ntype UnsafeSamplingManagerServer interface {\n\tmustEmbedUnimplementedSamplingManagerServer()\n}\n\nfunc RegisterSamplingManagerServer(s grpc.ServiceRegistrar, srv SamplingManagerServer) {\n\ts.RegisterService(&SamplingManager_ServiceDesc, srv)\n}\n\nfunc _SamplingManager_GetSamplingStrategy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(SamplingStrategyParameters)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(SamplingManagerServer).GetSamplingStrategy(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/jaeger.api_v2.SamplingManager/GetSamplingStrategy\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(SamplingManagerServer).GetSamplingStrategy(ctx, req.(*SamplingStrategyParameters))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// SamplingManager_ServiceDesc is the grpc.ServiceDesc for SamplingManager 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 SamplingManager_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"jaeger.api_v2.SamplingManager\",\n\tHandlerType: (*SamplingManagerServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"GetSamplingStrategy\",\n\t\t\tHandler:    _SamplingManager_GetSamplingStrategy_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"jaeger/sampling.proto\",\n}\n"
  },
  {
    "path": "chapter/11/ops/proto/ops.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.25.0-devel\n// \tprotoc        v3.18.0\n// source: ops.proto\n\npackage proto\n\nimport (\n\tmodel \"github.com/PacktPublishing/Go-for-DevOps/chapter/11/ops/proto/jaeger/model\"\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\tdurationpb \"google.golang.org/protobuf/types/known/durationpb\"\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 SamplerType int32\n\nconst (\n\tSamplerType_STUnknown SamplerType = 0\n\tSamplerType_STNever   SamplerType = 1\n\tSamplerType_STAlways  SamplerType = 2\n\tSamplerType_STFloat   SamplerType = 3\n)\n\n// Enum value maps for SamplerType.\nvar (\n\tSamplerType_name = map[int32]string{\n\t\t0: \"STUnknown\",\n\t\t1: \"STNever\",\n\t\t2: \"STAlways\",\n\t\t3: \"STFloat\",\n\t}\n\tSamplerType_value = map[string]int32{\n\t\t\"STUnknown\": 0,\n\t\t\"STNever\":   1,\n\t\t\"STAlways\":  2,\n\t\t\"STFloat\":   3,\n\t}\n)\n\nfunc (x SamplerType) Enum() *SamplerType {\n\tp := new(SamplerType)\n\t*p = x\n\treturn p\n}\n\nfunc (x SamplerType) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (SamplerType) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_ops_proto_enumTypes[0].Descriptor()\n}\n\nfunc (SamplerType) Type() protoreflect.EnumType {\n\treturn &file_ops_proto_enumTypes[0]\n}\n\nfunc (x SamplerType) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use SamplerType.Descriptor instead.\nfunc (SamplerType) EnumDescriptor() ([]byte, []int) {\n\treturn file_ops_proto_rawDescGZIP(), []int{0}\n}\n\n// The request to get traces from Jaeger.\ntype ListTracesReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The name of the service to find traces for.\n\tService string `protobuf:\"bytes,1,opt,name=service,proto3\" json:\"service,omitempty\"`\n\t// Filter the traces for this operation.\n\tOperation string `protobuf:\"bytes,2,opt,name=operation,proto3\" json:\"operation,omitempty\"`\n\t// Filter the traces for only traces with these all these tags.\n\tTags []string `protobuf:\"bytes,3,rep,name=tags,proto3\" json:\"tags,omitempty\"`\n\t// Traces must start after the time in unix nanoseconds.\n\tStart int64 `protobuf:\"varint,4,opt,name=start,proto3\" json:\"start,omitempty\"`\n\t// Traces must end before this time in unix nanoseconds.\n\tEnd int64 `protobuf:\"varint,5,opt,name=end,proto3\" json:\"end,omitempty\"`\n\t// The minimum duration of a matched trace.\n\tDurationMin int64 `protobuf:\"varint,6,opt,name=duration_min,json=durationMin,proto3\" json:\"duration_min,omitempty\"`\n\t// The maximum duration of a matched trace.\n\tDurationMax int64 `protobuf:\"varint,7,opt,name=duration_max,json=durationMax,proto3\" json:\"duration_max,omitempty\"`\n\t// The number of traces to return.\n\tSearchDepth int32 `protobuf:\"varint,8,opt,name=search_depth,json=searchDepth,proto3\" json:\"search_depth,omitempty\"`\n}\n\nfunc (x *ListTracesReq) Reset() {\n\t*x = ListTracesReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ops_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ListTracesReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ListTracesReq) ProtoMessage() {}\n\nfunc (x *ListTracesReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_ops_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 ListTracesReq.ProtoReflect.Descriptor instead.\nfunc (*ListTracesReq) Descriptor() ([]byte, []int) {\n\treturn file_ops_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *ListTracesReq) GetService() string {\n\tif x != nil {\n\t\treturn x.Service\n\t}\n\treturn \"\"\n}\n\nfunc (x *ListTracesReq) GetOperation() string {\n\tif x != nil {\n\t\treturn x.Operation\n\t}\n\treturn \"\"\n}\n\nfunc (x *ListTracesReq) GetTags() []string {\n\tif x != nil {\n\t\treturn x.Tags\n\t}\n\treturn nil\n}\n\nfunc (x *ListTracesReq) GetStart() int64 {\n\tif x != nil {\n\t\treturn x.Start\n\t}\n\treturn 0\n}\n\nfunc (x *ListTracesReq) GetEnd() int64 {\n\tif x != nil {\n\t\treturn x.End\n\t}\n\treturn 0\n}\n\nfunc (x *ListTracesReq) GetDurationMin() int64 {\n\tif x != nil {\n\t\treturn x.DurationMin\n\t}\n\treturn 0\n}\n\nfunc (x *ListTracesReq) GetDurationMax() int64 {\n\tif x != nil {\n\t\treturn x.DurationMax\n\t}\n\treturn 0\n}\n\nfunc (x *ListTracesReq) GetSearchDepth() int32 {\n\tif x != nil {\n\t\treturn x.SearchDepth\n\t}\n\treturn 0\n}\n\n// This represents a trace identifier and when the trace started.\ntype TraceItem struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The trace identifier in hex form.\n\tId string `protobuf:\"bytes,1,opt,name=id,proto3\" json:\"id,omitempty\"`\n\t// The time the trace started in unix nanosecods.\n\tStart int64 `protobuf:\"varint,2,opt,name=start,proto3\" json:\"start,omitempty\"`\n}\n\nfunc (x *TraceItem) Reset() {\n\t*x = TraceItem{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ops_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *TraceItem) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*TraceItem) ProtoMessage() {}\n\nfunc (x *TraceItem) ProtoReflect() protoreflect.Message {\n\tmi := &file_ops_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 TraceItem.ProtoReflect.Descriptor instead.\nfunc (*TraceItem) Descriptor() ([]byte, []int) {\n\treturn file_ops_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *TraceItem) GetId() string {\n\tif x != nil {\n\t\treturn x.Id\n\t}\n\treturn \"\"\n}\n\nfunc (x *TraceItem) GetStart() int64 {\n\tif x != nil {\n\t\treturn x.Start\n\t}\n\treturn 0\n}\n\n// A response to ListTraces().\ntype ListTracesResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// A list of traces that met the search criteria.\n\tTraces []*TraceItem `protobuf:\"bytes,1,rep,name=traces,proto3\" json:\"traces,omitempty\"`\n}\n\nfunc (x *ListTracesResp) Reset() {\n\t*x = ListTracesResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ops_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ListTracesResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ListTracesResp) ProtoMessage() {}\n\nfunc (x *ListTracesResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_ops_proto_msgTypes[2]\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 ListTracesResp.ProtoReflect.Descriptor instead.\nfunc (*ListTracesResp) Descriptor() ([]byte, []int) {\n\treturn file_ops_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *ListTracesResp) GetTraces() []*TraceItem {\n\tif x != nil {\n\t\treturn x.Traces\n\t}\n\treturn nil\n}\n\n// The request to get a URL showing the trace information for a trace id.\ntype ShowTraceReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The ID of the trace in hex.\n\tId string `protobuf:\"bytes,1,opt,name=id,proto3\" json:\"id,omitempty\"`\n}\n\nfunc (x *ShowTraceReq) Reset() {\n\t*x = ShowTraceReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ops_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ShowTraceReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ShowTraceReq) ProtoMessage() {}\n\nfunc (x *ShowTraceReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_ops_proto_msgTypes[3]\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 ShowTraceReq.ProtoReflect.Descriptor instead.\nfunc (*ShowTraceReq) Descriptor() ([]byte, []int) {\n\treturn file_ops_proto_rawDescGZIP(), []int{3}\n}\n\nfunc (x *ShowTraceReq) GetId() string {\n\tif x != nil {\n\t\treturn x.Id\n\t}\n\treturn \"\"\n}\n\n// The resonse to ShowTrace().\ntype ShowTraceResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The URL to view the trace.\n\tId string `protobuf:\"bytes,1,opt,name=id,proto3\" json:\"id,omitempty\"`\n\t// The name of the operations being performed.\n\tOperations []string `protobuf:\"bytes,2,rep,name=operations,proto3\" json:\"operations,omitempty\"`\n\t// A list of tag values in spans labelled \"error\".\n\tErrors []string `protobuf:\"bytes,3,rep,name=errors,proto3\" json:\"errors,omitempty\"`\n\t// A list of all tags in the spans.\n\tTags []string `protobuf:\"bytes,4,rep,name=tags,proto3\" json:\"tags,omitempty\"`\n\t// The longest duration found in any span.\n\tDuration *durationpb.Duration `protobuf:\"bytes,5,opt,name=duration,proto3\" json:\"duration,omitempty\"`\n}\n\nfunc (x *ShowTraceResp) Reset() {\n\t*x = ShowTraceResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ops_proto_msgTypes[4]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ShowTraceResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ShowTraceResp) ProtoMessage() {}\n\nfunc (x *ShowTraceResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_ops_proto_msgTypes[4]\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 ShowTraceResp.ProtoReflect.Descriptor instead.\nfunc (*ShowTraceResp) Descriptor() ([]byte, []int) {\n\treturn file_ops_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (x *ShowTraceResp) GetId() string {\n\tif x != nil {\n\t\treturn x.Id\n\t}\n\treturn \"\"\n}\n\nfunc (x *ShowTraceResp) GetOperations() []string {\n\tif x != nil {\n\t\treturn x.Operations\n\t}\n\treturn nil\n}\n\nfunc (x *ShowTraceResp) GetErrors() []string {\n\tif x != nil {\n\t\treturn x.Errors\n\t}\n\treturn nil\n}\n\nfunc (x *ShowTraceResp) GetTags() []string {\n\tif x != nil {\n\t\treturn x.Tags\n\t}\n\treturn nil\n}\n\nfunc (x *ShowTraceResp) GetDuration() *durationpb.Duration {\n\tif x != nil {\n\t\treturn x.Duration\n\t}\n\treturn nil\n}\n\ntype ShowLogsReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The hex ID of the trace.\n\tId string `protobuf:\"bytes,1,opt,name=id,proto3\" json:\"id,omitempty\"`\n}\n\nfunc (x *ShowLogsReq) Reset() {\n\t*x = ShowLogsReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ops_proto_msgTypes[5]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ShowLogsReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ShowLogsReq) ProtoMessage() {}\n\nfunc (x *ShowLogsReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_ops_proto_msgTypes[5]\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 ShowLogsReq.ProtoReflect.Descriptor instead.\nfunc (*ShowLogsReq) Descriptor() ([]byte, []int) {\n\treturn file_ops_proto_rawDescGZIP(), []int{5}\n}\n\nfunc (x *ShowLogsReq) GetId() string {\n\tif x != nil {\n\t\treturn x.Id\n\t}\n\treturn \"\"\n}\n\ntype ShowLogsResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tId   string       `protobuf:\"bytes,1,opt,name=id,proto3\" json:\"id,omitempty\"`\n\tLogs []*model.Log `protobuf:\"bytes,2,rep,name=logs,proto3\" json:\"logs,omitempty\"`\n}\n\nfunc (x *ShowLogsResp) Reset() {\n\t*x = ShowLogsResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ops_proto_msgTypes[6]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ShowLogsResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ShowLogsResp) ProtoMessage() {}\n\nfunc (x *ShowLogsResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_ops_proto_msgTypes[6]\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 ShowLogsResp.ProtoReflect.Descriptor instead.\nfunc (*ShowLogsResp) Descriptor() ([]byte, []int) {\n\treturn file_ops_proto_rawDescGZIP(), []int{6}\n}\n\nfunc (x *ShowLogsResp) GetId() string {\n\tif x != nil {\n\t\treturn x.Id\n\t}\n\treturn \"\"\n}\n\nfunc (x *ShowLogsResp) GetLogs() []*model.Log {\n\tif x != nil {\n\t\treturn x.Logs\n\t}\n\treturn nil\n}\n\n// Used to request we change the OTEL sampling.\ntype ChangeSamplingReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The type of sampling to change to.\n\tType SamplerType `protobuf:\"varint,1,opt,name=type,proto3,enum=ops.SamplerType\" json:\"type,omitempty\"`\n\t// This is the sampling rate if type == STFloat. Values must be\n\t// > 0 and <= 1.0 .\n\tFloatValue float64 `protobuf:\"fixed64,2,opt,name=float_value,json=floatValue,proto3\" json:\"float_value,omitempty\"`\n}\n\nfunc (x *ChangeSamplingReq) Reset() {\n\t*x = ChangeSamplingReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ops_proto_msgTypes[7]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ChangeSamplingReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ChangeSamplingReq) ProtoMessage() {}\n\nfunc (x *ChangeSamplingReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_ops_proto_msgTypes[7]\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 ChangeSamplingReq.ProtoReflect.Descriptor instead.\nfunc (*ChangeSamplingReq) Descriptor() ([]byte, []int) {\n\treturn file_ops_proto_rawDescGZIP(), []int{7}\n}\n\nfunc (x *ChangeSamplingReq) GetType() SamplerType {\n\tif x != nil {\n\t\treturn x.Type\n\t}\n\treturn SamplerType_STUnknown\n}\n\nfunc (x *ChangeSamplingReq) GetFloatValue() float64 {\n\tif x != nil {\n\t\treturn x.FloatValue\n\t}\n\treturn 0\n}\n\n// The response to a sampling change.\ntype ChangeSamplingResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ChangeSamplingResp) Reset() {\n\t*x = ChangeSamplingResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ops_proto_msgTypes[8]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ChangeSamplingResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ChangeSamplingResp) ProtoMessage() {}\n\nfunc (x *ChangeSamplingResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_ops_proto_msgTypes[8]\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 ChangeSamplingResp.ProtoReflect.Descriptor instead.\nfunc (*ChangeSamplingResp) Descriptor() ([]byte, []int) {\n\treturn file_ops_proto_rawDescGZIP(), []int{8}\n}\n\n// The request to get the deployed version of the service.\ntype DeployedVersionReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *DeployedVersionReq) Reset() {\n\t*x = DeployedVersionReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ops_proto_msgTypes[9]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DeployedVersionReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DeployedVersionReq) ProtoMessage() {}\n\nfunc (x *DeployedVersionReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_ops_proto_msgTypes[9]\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 DeployedVersionReq.ProtoReflect.Descriptor instead.\nfunc (*DeployedVersionReq) Descriptor() ([]byte, []int) {\n\treturn file_ops_proto_rawDescGZIP(), []int{9}\n}\n\n// The response to DeployedVersion().\ntype DeployedVersionResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The version running according to prometheus metrics.\n\tVersion string `protobuf:\"bytes,1,opt,name=version,proto3\" json:\"version,omitempty\"`\n}\n\nfunc (x *DeployedVersionResp) Reset() {\n\t*x = DeployedVersionResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ops_proto_msgTypes[10]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DeployedVersionResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DeployedVersionResp) ProtoMessage() {}\n\nfunc (x *DeployedVersionResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_ops_proto_msgTypes[10]\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 DeployedVersionResp.ProtoReflect.Descriptor instead.\nfunc (*DeployedVersionResp) Descriptor() ([]byte, []int) {\n\treturn file_ops_proto_rawDescGZIP(), []int{10}\n}\n\nfunc (x *DeployedVersionResp) GetVersion() string {\n\tif x != nil {\n\t\treturn x.Version\n\t}\n\treturn \"\"\n}\n\n// Alert describes a Prometheus alert.\ntype Alert struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// This is the current state of the alert.\n\tState string `protobuf:\"bytes,1,opt,name=state,proto3\" json:\"state,omitempty\"`\n\t// This is the current value of the alert.\n\tValue string `protobuf:\"bytes,2,opt,name=value,proto3\" json:\"value,omitempty\"`\n\t// This is how long it has been active in unix nanoseconds.\n\tActiveAt int64 `protobuf:\"varint,3,opt,name=active_at,json=activeAt,proto3\" json:\"active_at,omitempty\"`\n}\n\nfunc (x *Alert) Reset() {\n\t*x = Alert{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ops_proto_msgTypes[11]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Alert) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Alert) ProtoMessage() {}\n\nfunc (x *Alert) ProtoReflect() protoreflect.Message {\n\tmi := &file_ops_proto_msgTypes[11]\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 Alert.ProtoReflect.Descriptor instead.\nfunc (*Alert) Descriptor() ([]byte, []int) {\n\treturn file_ops_proto_rawDescGZIP(), []int{11}\n}\n\nfunc (x *Alert) GetState() string {\n\tif x != nil {\n\t\treturn x.State\n\t}\n\treturn \"\"\n}\n\nfunc (x *Alert) GetValue() string {\n\tif x != nil {\n\t\treturn x.Value\n\t}\n\treturn \"\"\n}\n\nfunc (x *Alert) GetActiveAt() int64 {\n\tif x != nil {\n\t\treturn x.ActiveAt\n\t}\n\treturn 0\n}\n\n// This requests an set of active alerts in the system.\ntype AlertsReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Labels that the alert must match. Must have all labels. None indicates all alerts.\n\tLabels []string `protobuf:\"bytes,1,rep,name=labels,proto3\" json:\"labels,omitempty\"`\n\t// It must be an alert that is active since this time in unix nanoseconds. 0 indicates any alive alert.\n\tActiveAt int64 `protobuf:\"varint,2,opt,name=active_at,json=activeAt,proto3\" json:\"active_at,omitempty\"`\n\t// It must have one of these states. None indicates all states.\n\tStates []string `protobuf:\"bytes,3,rep,name=states,proto3\" json:\"states,omitempty\"`\n}\n\nfunc (x *AlertsReq) Reset() {\n\t*x = AlertsReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ops_proto_msgTypes[12]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AlertsReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AlertsReq) ProtoMessage() {}\n\nfunc (x *AlertsReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_ops_proto_msgTypes[12]\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 AlertsReq.ProtoReflect.Descriptor instead.\nfunc (*AlertsReq) Descriptor() ([]byte, []int) {\n\treturn file_ops_proto_rawDescGZIP(), []int{12}\n}\n\nfunc (x *AlertsReq) GetLabels() []string {\n\tif x != nil {\n\t\treturn x.Labels\n\t}\n\treturn nil\n}\n\nfunc (x *AlertsReq) GetActiveAt() int64 {\n\tif x != nil {\n\t\treturn x.ActiveAt\n\t}\n\treturn 0\n}\n\nfunc (x *AlertsReq) GetStates() []string {\n\tif x != nil {\n\t\treturn x.States\n\t}\n\treturn nil\n}\n\n// The response to Alerts().\ntype AlertsResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// A list of alerts that matched the filter.\n\tAlerts []*Alert `protobuf:\"bytes,1,rep,name=alerts,proto3\" json:\"alerts,omitempty\"`\n}\n\nfunc (x *AlertsResp) Reset() {\n\t*x = AlertsResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_ops_proto_msgTypes[13]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AlertsResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AlertsResp) ProtoMessage() {}\n\nfunc (x *AlertsResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_ops_proto_msgTypes[13]\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 AlertsResp.ProtoReflect.Descriptor instead.\nfunc (*AlertsResp) Descriptor() ([]byte, []int) {\n\treturn file_ops_proto_rawDescGZIP(), []int{13}\n}\n\nfunc (x *AlertsResp) GetAlerts() []*Alert {\n\tif x != nil {\n\t\treturn x.Alerts\n\t}\n\treturn nil\n}\n\nvar File_ops_proto protoreflect.FileDescriptor\n\nvar file_ops_proto_rawDesc = []byte{\n\t0x0a, 0x09, 0x6f, 0x70, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x6f, 0x70, 0x73,\n\t0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,\n\t0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x1a, 0x18, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2f, 0x6d,\n\t0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xec, 0x01, 0x0a, 0x0d, 0x4c,\n\t0x69, 0x73, 0x74, 0x54, 0x72, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07,\n\t0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73,\n\t0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,\n\t0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61,\n\t0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03,\n\t0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72,\n\t0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10,\n\t0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x65, 0x6e, 0x64,\n\t0x12, 0x21, 0x0a, 0x0c, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x69, 0x6e,\n\t0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,\n\t0x4d, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f,\n\t0x6d, 0x61, 0x78, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x64, 0x75, 0x72, 0x61, 0x74,\n\t0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68,\n\t0x5f, 0x64, 0x65, 0x70, 0x74, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x73, 0x65,\n\t0x61, 0x72, 0x63, 0x68, 0x44, 0x65, 0x70, 0x74, 0x68, 0x22, 0x31, 0x0a, 0x09, 0x54, 0x72, 0x61,\n\t0x63, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x22, 0x38, 0x0a, 0x0e,\n\t0x4c, 0x69, 0x73, 0x74, 0x54, 0x72, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x26,\n\t0x0a, 0x06, 0x74, 0x72, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e,\n\t0x2e, 0x6f, 0x70, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06,\n\t0x74, 0x72, 0x61, 0x63, 0x65, 0x73, 0x22, 0x1e, 0x0a, 0x0c, 0x53, 0x68, 0x6f, 0x77, 0x54, 0x72,\n\t0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0xa2, 0x01, 0x0a, 0x0d, 0x53, 0x68, 0x6f, 0x77, 0x54,\n\t0x72, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,\n\t0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72,\n\t0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x70,\n\t0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f,\n\t0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73,\n\t0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04,\n\t0x74, 0x61, 0x67, 0x73, 0x12, 0x35, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,\n\t0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,\n\t0x6e, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x1d, 0x0a, 0x0b, 0x53,\n\t0x68, 0x6f, 0x77, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x46, 0x0a, 0x0c, 0x53, 0x68,\n\t0x6f, 0x77, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x26, 0x0a, 0x04, 0x6c, 0x6f,\n\t0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6a, 0x61, 0x65, 0x67, 0x65,\n\t0x72, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f,\n\t0x67, 0x73, 0x22, 0x5a, 0x0a, 0x11, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x61, 0x6d, 0x70,\n\t0x6c, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x12, 0x24, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18,\n\t0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x6f, 0x70, 0x73, 0x2e, 0x53, 0x61, 0x6d, 0x70,\n\t0x6c, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a,\n\t0x0b, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01,\n\t0x28, 0x01, 0x52, 0x0a, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x14,\n\t0x0a, 0x12, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67,\n\t0x52, 0x65, 0x73, 0x70, 0x22, 0x14, 0x0a, 0x12, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x65, 0x64,\n\t0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x22, 0x2f, 0x0a, 0x13, 0x44, 0x65,\n\t0x70, 0x6c, 0x6f, 0x79, 0x65, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73,\n\t0x70, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x50, 0x0a, 0x05, 0x41,\n\t0x6c, 0x65, 0x72, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,\n\t0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,\n\t0x12, 0x1b, 0x0a, 0x09, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20,\n\t0x01, 0x28, 0x03, 0x52, 0x08, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x41, 0x74, 0x22, 0x58, 0x0a,\n\t0x09, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x61,\n\t0x62, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65,\n\t0x6c, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x61, 0x74, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x41, 0x74, 0x12,\n\t0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52,\n\t0x06, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x22, 0x30, 0x0a, 0x0a, 0x41, 0x6c, 0x65, 0x72, 0x74,\n\t0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x22, 0x0a, 0x06, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x18,\n\t0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x6f, 0x70, 0x73, 0x2e, 0x41, 0x6c, 0x65, 0x72,\n\t0x74, 0x52, 0x06, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x2a, 0x44, 0x0a, 0x0b, 0x53, 0x61, 0x6d,\n\t0x70, 0x6c, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x54, 0x55, 0x6e,\n\t0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x4e, 0x65, 0x76,\n\t0x65, 0x72, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x54, 0x41, 0x6c, 0x77, 0x61, 0x79, 0x73,\n\t0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x10, 0x03, 0x32,\n\t0xe1, 0x02, 0x0a, 0x03, 0x4f, 0x70, 0x73, 0x12, 0x37, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x54,\n\t0x72, 0x61, 0x63, 0x65, 0x73, 0x12, 0x12, 0x2e, 0x6f, 0x70, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74,\n\t0x54, 0x72, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x6f, 0x70, 0x73, 0x2e,\n\t0x4c, 0x69, 0x73, 0x74, 0x54, 0x72, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00,\n\t0x12, 0x34, 0x0a, 0x09, 0x53, 0x68, 0x6f, 0x77, 0x54, 0x72, 0x61, 0x63, 0x65, 0x12, 0x11, 0x2e,\n\t0x6f, 0x70, 0x73, 0x2e, 0x53, 0x68, 0x6f, 0x77, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71,\n\t0x1a, 0x12, 0x2e, 0x6f, 0x70, 0x73, 0x2e, 0x53, 0x68, 0x6f, 0x77, 0x54, 0x72, 0x61, 0x63, 0x65,\n\t0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x08, 0x53, 0x68, 0x6f, 0x77, 0x4c, 0x6f,\n\t0x67, 0x73, 0x12, 0x10, 0x2e, 0x6f, 0x70, 0x73, 0x2e, 0x53, 0x68, 0x6f, 0x77, 0x4c, 0x6f, 0x67,\n\t0x73, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x6f, 0x70, 0x73, 0x2e, 0x53, 0x68, 0x6f, 0x77, 0x4c,\n\t0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x0e, 0x43, 0x68, 0x61,\n\t0x6e, 0x67, 0x65, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x12, 0x16, 0x2e, 0x6f, 0x70,\n\t0x73, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67,\n\t0x52, 0x65, 0x71, 0x1a, 0x17, 0x2e, 0x6f, 0x70, 0x73, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65,\n\t0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x46,\n\t0x0a, 0x0f, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x65, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,\n\t0x6e, 0x12, 0x17, 0x2e, 0x6f, 0x70, 0x73, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x65, 0x64,\n\t0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x6f, 0x70, 0x73,\n\t0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x65, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,\n\t0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x2b, 0x0a, 0x06, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73,\n\t0x12, 0x0e, 0x2e, 0x6f, 0x70, 0x73, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x71,\n\t0x1a, 0x0f, 0x2e, 0x6f, 0x70, 0x73, 0x2e, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73,\n\t0x70, 0x22, 0x00, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,\n\t0x6d, 0x2f, 0x50, 0x61, 0x63, 0x6b, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x69, 0x6e,\n\t0x67, 0x2f, 0x47, 0x6f, 0x2d, 0x66, 0x6f, 0x72, 0x2d, 0x44, 0x65, 0x76, 0x4f, 0x70, 0x73, 0x2f,\n\t0x63, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72, 0x2f, 0x31, 0x30, 0x2f, 0x6f, 0x70, 0x73, 0x2f, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_ops_proto_rawDescOnce sync.Once\n\tfile_ops_proto_rawDescData = file_ops_proto_rawDesc\n)\n\nfunc file_ops_proto_rawDescGZIP() []byte {\n\tfile_ops_proto_rawDescOnce.Do(func() {\n\t\tfile_ops_proto_rawDescData = protoimpl.X.CompressGZIP(file_ops_proto_rawDescData)\n\t})\n\treturn file_ops_proto_rawDescData\n}\n\nvar file_ops_proto_enumTypes = make([]protoimpl.EnumInfo, 1)\nvar file_ops_proto_msgTypes = make([]protoimpl.MessageInfo, 14)\nvar file_ops_proto_goTypes = []interface{}{\n\t(SamplerType)(0),            // 0: ops.SamplerType\n\t(*ListTracesReq)(nil),       // 1: ops.ListTracesReq\n\t(*TraceItem)(nil),           // 2: ops.TraceItem\n\t(*ListTracesResp)(nil),      // 3: ops.ListTracesResp\n\t(*ShowTraceReq)(nil),        // 4: ops.ShowTraceReq\n\t(*ShowTraceResp)(nil),       // 5: ops.ShowTraceResp\n\t(*ShowLogsReq)(nil),         // 6: ops.ShowLogsReq\n\t(*ShowLogsResp)(nil),        // 7: ops.ShowLogsResp\n\t(*ChangeSamplingReq)(nil),   // 8: ops.ChangeSamplingReq\n\t(*ChangeSamplingResp)(nil),  // 9: ops.ChangeSamplingResp\n\t(*DeployedVersionReq)(nil),  // 10: ops.DeployedVersionReq\n\t(*DeployedVersionResp)(nil), // 11: ops.DeployedVersionResp\n\t(*Alert)(nil),               // 12: ops.Alert\n\t(*AlertsReq)(nil),           // 13: ops.AlertsReq\n\t(*AlertsResp)(nil),          // 14: ops.AlertsResp\n\t(*durationpb.Duration)(nil), // 15: google.protobuf.Duration\n\t(*model.Log)(nil),           // 16: jaeger.api_v2.Log\n}\nvar file_ops_proto_depIdxs = []int32{\n\t2,  // 0: ops.ListTracesResp.traces:type_name -> ops.TraceItem\n\t15, // 1: ops.ShowTraceResp.duration:type_name -> google.protobuf.Duration\n\t16, // 2: ops.ShowLogsResp.logs:type_name -> jaeger.api_v2.Log\n\t0,  // 3: ops.ChangeSamplingReq.type:type_name -> ops.SamplerType\n\t12, // 4: ops.AlertsResp.alerts:type_name -> ops.Alert\n\t1,  // 5: ops.Ops.ListTraces:input_type -> ops.ListTracesReq\n\t4,  // 6: ops.Ops.ShowTrace:input_type -> ops.ShowTraceReq\n\t6,  // 7: ops.Ops.ShowLogs:input_type -> ops.ShowLogsReq\n\t8,  // 8: ops.Ops.ChangeSampling:input_type -> ops.ChangeSamplingReq\n\t10, // 9: ops.Ops.DeployedVersion:input_type -> ops.DeployedVersionReq\n\t13, // 10: ops.Ops.Alerts:input_type -> ops.AlertsReq\n\t3,  // 11: ops.Ops.ListTraces:output_type -> ops.ListTracesResp\n\t5,  // 12: ops.Ops.ShowTrace:output_type -> ops.ShowTraceResp\n\t7,  // 13: ops.Ops.ShowLogs:output_type -> ops.ShowLogsResp\n\t9,  // 14: ops.Ops.ChangeSampling:output_type -> ops.ChangeSamplingResp\n\t11, // 15: ops.Ops.DeployedVersion:output_type -> ops.DeployedVersionResp\n\t14, // 16: ops.Ops.Alerts:output_type -> ops.AlertsResp\n\t11, // [11:17] is the sub-list for method output_type\n\t5,  // [5:11] is the sub-list for method input_type\n\t5,  // [5:5] is the sub-list for extension type_name\n\t5,  // [5:5] is the sub-list for extension extendee\n\t0,  // [0:5] is the sub-list for field type_name\n}\n\nfunc init() { file_ops_proto_init() }\nfunc file_ops_proto_init() {\n\tif File_ops_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_ops_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ListTracesReq); 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_ops_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*TraceItem); 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_ops_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ListTracesResp); 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_ops_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ShowTraceReq); 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_ops_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ShowTraceResp); 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_ops_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ShowLogsReq); 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_ops_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ShowLogsResp); 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_ops_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ChangeSamplingReq); 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_ops_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ChangeSamplingResp); 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_ops_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DeployedVersionReq); 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_ops_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DeployedVersionResp); 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_ops_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Alert); 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_ops_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*AlertsReq); 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_ops_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*AlertsResp); 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_ops_proto_rawDesc,\n\t\t\tNumEnums:      1,\n\t\t\tNumMessages:   14,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_ops_proto_goTypes,\n\t\tDependencyIndexes: file_ops_proto_depIdxs,\n\t\tEnumInfos:         file_ops_proto_enumTypes,\n\t\tMessageInfos:      file_ops_proto_msgTypes,\n\t}.Build()\n\tFile_ops_proto = out.File\n\tfile_ops_proto_rawDesc = nil\n\tfile_ops_proto_goTypes = nil\n\tfile_ops_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "chapter/11/ops/proto/ops.proto",
    "content": "syntax = \"proto3\";\n\npackage ops;\n\nimport \"google/protobuf/duration.proto\";\nimport \"jaeger/model/model.proto\";\n\noption go_package = \"github.com/PacktPublishing/Go-for-DevOps/chapter/10/ops/proto\";\n\n// The request to get traces from Jaeger.\nmessage ListTracesReq {\n\t// The name of the service to find traces for.\n\tstring service = 1;\n\t// Filter the traces for this operation.\n\tstring operation = 2;\n\t// Filter the traces for only traces with these all these tags.\n\trepeated string tags = 3;\n\t// Traces must start after the time in unix nanoseconds.\n\tint64 start = 4;\n\t// Traces must end before this time in unix nanoseconds.\n\tint64 end = 5;\n\t// The minimum duration of a matched trace.\n\tint64 duration_min = 6;\n\t// The maximum duration of a matched trace.\n\tint64 duration_max = 7;\n\t// The number of traces to return.\n\tint32 search_depth = 8;\n}\n\n// This represents a trace identifier and when the trace started.\nmessage TraceItem {\n\t// The trace identifier in hex form.\n\tstring id = 1;\n\t// The time the trace started in unix nanosecods.\n\tint64 start = 2;\n}\t\n\n// A response to ListTraces().\nmessage ListTracesResp {\n\t// A list of traces that met the search criteria.\n\trepeated TraceItem traces = 1;\n}\n\n// The request to get a URL showing the trace information for a trace id.\nmessage ShowTraceReq {\n\t// The ID of the trace in hex.\n\tstring id = 1;\n}\n\n// The resonse to ShowTrace().\nmessage ShowTraceResp {\n\t// The URL to view the trace.\n\tstring id = 1;\n\t// The name of the operations being performed.\n\trepeated string operations = 2;\n\t// A list of tag values in spans labelled \"error\".\n\trepeated string errors = 3;\n\t// A list of all tags in the spans.\n\trepeated string tags = 4;\n\t// The longest duration found in any span.\n\tgoogle.protobuf.Duration duration = 5;\n}\n\nmessage ShowLogsReq {\n\t// The hex ID of the trace.\n\tstring id = 1;\n}\n\nmessage ShowLogsResp{\n\tstring id = 1;\n\trepeated jaeger.api_v2.Log logs = 2;\n}\n\nenum SamplerType {\n\tSTUnknown = 0;\n\tSTNever = 1;\n\tSTAlways = 2;\n\tSTFloat = 3;\n}\n\n// Used to request we change the OTEL sampling.\nmessage ChangeSamplingReq {\n\t// The type of sampling to change to.\n\tSamplerType type = 1;\n\n\t// This is the sampling rate if type == STFloat. Values must be\n\t// > 0 and <= 1.0 .\t\n\tdouble float_value = 2;\n}\n\n// The response to a sampling change.\nmessage ChangeSamplingResp{}\n\n// The request to get the deployed version of the service.\nmessage DeployedVersionReq {}\n\n// The response to DeployedVersion().\nmessage DeployedVersionResp {\n\t// The version running according to prometheus metrics.\n\tstring version = 1;\n}\n\n// Alert describes a Prometheus alert.\nmessage Alert {\n\t// This is the current state of the alert.\n\tstring state = 1;\n\t// This is the current value of the alert.\n\tstring value = 2;\n\t// This is how long it has been active in unix nanoseconds.\n\tint64 active_at = 3;\n}\n\n// This requests an set of active alerts in the system.\nmessage AlertsReq{\n\t// Labels that the alert must match. Must have all labels. None indicates all alerts.\n\trepeated string labels = 1;\n\t// It must be an alert that is active since this time in unix nanoseconds. 0 indicates any alive alert.\n\tint64 active_at = 2;\n\t// It must have one of these states. None indicates all states.\n\trepeated string states = 3;\n}\n\n// The response to Alerts().\nmessage AlertsResp{\n\t// A list of alerts that matched the filter.\n\trepeated Alert alerts = 1;\n}\n\nservice Ops {\n\t// ListTraces that the Jaeger has for our application.\n\trpc ListTraces(ListTracesReq) returns (ListTracesResp) {};\n\t// ShowTrace returns the URL of a trace you ask for.\n\trpc ShowTrace(ShowTraceReq) returns (ShowTraceResp) {};\n\t// ShowLogs extracts the logs from a trace.\n\trpc ShowLogs(ShowLogsReq) returns (ShowLogsResp) {};\n\t// ChangeSampling changes the sampling the service is currently using for its traces.\n\trpc ChangeSampling(ChangeSamplingReq) returns (ChangeSamplingResp) {};\n\t// DeployedVersion returns the currently deployed version of the application.\n\trpc DeployedVersion(DeployedVersionReq) returns (DeployedVersionResp) {};\n\t// Alerts returns the currently firing alerts.\n\trpc Alerts(AlertsReq) returns (AlertsResp) {};\n}\n"
  },
  {
    "path": "chapter/11/ops/proto/ops_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n\npackage proto\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// OpsClient is the client API for Ops 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 OpsClient interface {\n\t// ListTraces that the Jaeger has for our application.\n\tListTraces(ctx context.Context, in *ListTracesReq, opts ...grpc.CallOption) (*ListTracesResp, error)\n\t// ShowTrace returns the URL of a trace you ask for.\n\tShowTrace(ctx context.Context, in *ShowTraceReq, opts ...grpc.CallOption) (*ShowTraceResp, error)\n\t// ShowLogs extracts the logs from a trace.\n\tShowLogs(ctx context.Context, in *ShowLogsReq, opts ...grpc.CallOption) (*ShowLogsResp, error)\n\t// ChangeSampling changes the sampling the service is currently using for its traces.\n\tChangeSampling(ctx context.Context, in *ChangeSamplingReq, opts ...grpc.CallOption) (*ChangeSamplingResp, error)\n\t// DeployedVersion returns the currently deployed version of the application.\n\tDeployedVersion(ctx context.Context, in *DeployedVersionReq, opts ...grpc.CallOption) (*DeployedVersionResp, error)\n\t// Alerts returns the currently firing alerts.\n\tAlerts(ctx context.Context, in *AlertsReq, opts ...grpc.CallOption) (*AlertsResp, error)\n}\n\ntype opsClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewOpsClient(cc grpc.ClientConnInterface) OpsClient {\n\treturn &opsClient{cc}\n}\n\nfunc (c *opsClient) ListTraces(ctx context.Context, in *ListTracesReq, opts ...grpc.CallOption) (*ListTracesResp, error) {\n\tout := new(ListTracesResp)\n\terr := c.cc.Invoke(ctx, \"/ops.Ops/ListTraces\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *opsClient) ShowTrace(ctx context.Context, in *ShowTraceReq, opts ...grpc.CallOption) (*ShowTraceResp, error) {\n\tout := new(ShowTraceResp)\n\terr := c.cc.Invoke(ctx, \"/ops.Ops/ShowTrace\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *opsClient) ShowLogs(ctx context.Context, in *ShowLogsReq, opts ...grpc.CallOption) (*ShowLogsResp, error) {\n\tout := new(ShowLogsResp)\n\terr := c.cc.Invoke(ctx, \"/ops.Ops/ShowLogs\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *opsClient) ChangeSampling(ctx context.Context, in *ChangeSamplingReq, opts ...grpc.CallOption) (*ChangeSamplingResp, error) {\n\tout := new(ChangeSamplingResp)\n\terr := c.cc.Invoke(ctx, \"/ops.Ops/ChangeSampling\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *opsClient) DeployedVersion(ctx context.Context, in *DeployedVersionReq, opts ...grpc.CallOption) (*DeployedVersionResp, error) {\n\tout := new(DeployedVersionResp)\n\terr := c.cc.Invoke(ctx, \"/ops.Ops/DeployedVersion\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *opsClient) Alerts(ctx context.Context, in *AlertsReq, opts ...grpc.CallOption) (*AlertsResp, error) {\n\tout := new(AlertsResp)\n\terr := c.cc.Invoke(ctx, \"/ops.Ops/Alerts\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// OpsServer is the server API for Ops service.\n// All implementations must embed UnimplementedOpsServer\n// for forward compatibility\ntype OpsServer interface {\n\t// ListTraces that the Jaeger has for our application.\n\tListTraces(context.Context, *ListTracesReq) (*ListTracesResp, error)\n\t// ShowTrace returns the URL of a trace you ask for.\n\tShowTrace(context.Context, *ShowTraceReq) (*ShowTraceResp, error)\n\t// ShowLogs extracts the logs from a trace.\n\tShowLogs(context.Context, *ShowLogsReq) (*ShowLogsResp, error)\n\t// ChangeSampling changes the sampling the service is currently using for its traces.\n\tChangeSampling(context.Context, *ChangeSamplingReq) (*ChangeSamplingResp, error)\n\t// DeployedVersion returns the currently deployed version of the application.\n\tDeployedVersion(context.Context, *DeployedVersionReq) (*DeployedVersionResp, error)\n\t// Alerts returns the currently firing alerts.\n\tAlerts(context.Context, *AlertsReq) (*AlertsResp, error)\n\tmustEmbedUnimplementedOpsServer()\n}\n\n// UnimplementedOpsServer must be embedded to have forward compatible implementations.\ntype UnimplementedOpsServer struct {\n}\n\nfunc (UnimplementedOpsServer) ListTraces(context.Context, *ListTracesReq) (*ListTracesResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ListTraces not implemented\")\n}\nfunc (UnimplementedOpsServer) ShowTrace(context.Context, *ShowTraceReq) (*ShowTraceResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ShowTrace not implemented\")\n}\nfunc (UnimplementedOpsServer) ShowLogs(context.Context, *ShowLogsReq) (*ShowLogsResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ShowLogs not implemented\")\n}\nfunc (UnimplementedOpsServer) ChangeSampling(context.Context, *ChangeSamplingReq) (*ChangeSamplingResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ChangeSampling not implemented\")\n}\nfunc (UnimplementedOpsServer) DeployedVersion(context.Context, *DeployedVersionReq) (*DeployedVersionResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method DeployedVersion not implemented\")\n}\nfunc (UnimplementedOpsServer) Alerts(context.Context, *AlertsReq) (*AlertsResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method Alerts not implemented\")\n}\nfunc (UnimplementedOpsServer) mustEmbedUnimplementedOpsServer() {}\n\n// UnsafeOpsServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to OpsServer will\n// result in compilation errors.\ntype UnsafeOpsServer interface {\n\tmustEmbedUnimplementedOpsServer()\n}\n\nfunc RegisterOpsServer(s grpc.ServiceRegistrar, srv OpsServer) {\n\ts.RegisterService(&Ops_ServiceDesc, srv)\n}\n\nfunc _Ops_ListTraces_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ListTracesReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(OpsServer).ListTraces(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/ops.Ops/ListTraces\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(OpsServer).ListTraces(ctx, req.(*ListTracesReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Ops_ShowTrace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ShowTraceReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(OpsServer).ShowTrace(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/ops.Ops/ShowTrace\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(OpsServer).ShowTrace(ctx, req.(*ShowTraceReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Ops_ShowLogs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ShowLogsReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(OpsServer).ShowLogs(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/ops.Ops/ShowLogs\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(OpsServer).ShowLogs(ctx, req.(*ShowLogsReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Ops_ChangeSampling_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ChangeSamplingReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(OpsServer).ChangeSampling(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/ops.Ops/ChangeSampling\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(OpsServer).ChangeSampling(ctx, req.(*ChangeSamplingReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Ops_DeployedVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(DeployedVersionReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(OpsServer).DeployedVersion(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/ops.Ops/DeployedVersion\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(OpsServer).DeployedVersion(ctx, req.(*DeployedVersionReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Ops_Alerts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(AlertsReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(OpsServer).Alerts(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/ops.Ops/Alerts\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(OpsServer).Alerts(ctx, req.(*AlertsReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// Ops_ServiceDesc is the grpc.ServiceDesc for Ops 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 Ops_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"ops.Ops\",\n\tHandlerType: (*OpsServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"ListTraces\",\n\t\t\tHandler:    _Ops_ListTraces_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ShowTrace\",\n\t\t\tHandler:    _Ops_ShowTrace_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ShowLogs\",\n\t\t\tHandler:    _Ops_ShowLogs_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ChangeSampling\",\n\t\t\tHandler:    _Ops_ChangeSampling_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"DeployedVersion\",\n\t\t\tHandler:    _Ops_DeployedVersion_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"Alerts\",\n\t\t\tHandler:    _Ops_Alerts_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"ops.proto\",\n}\n"
  },
  {
    "path": "chapter/11/otel-collector-config.yaml",
    "content": "receivers:\n  otlp:\n    protocols:\n      grpc:\n\nexporters:\n  prometheus:\n    endpoint: \"0.0.0.0:8889\"\n    const_labels:\n      label1: value1\n  logging:\n\n  jaeger:\n    endpoint: jaeger-all-in-one:14250\n    tls:\n      insecure: true\n\nprocessors:\n  batch:\n\nservice:\n  pipelines:\n    traces:\n      receivers: [otlp]\n      processors: [batch]\n      exporters: [jaeger]\n    metrics:\n      receivers: [otlp]\n      processors: [batch]\n      exporters: [prometheus]\n"
  },
  {
    "path": "chapter/11/petstore/Dockerfile",
    "content": "FROM golang:1.17\nCOPY . /usr/src/server/\nWORKDIR /usr/src/server/\nRUN go install\nCMD [\"/go/bin/petstore\", \"--grpcTraces\", \"--traceSampling=.1\"]\n"
  },
  {
    "path": "chapter/11/petstore/README.md",
    "content": "# Petstore Application\n\n## Introduction\n\nIn chapter 8, you have seen an example application that did metrics and tracing using Open Telemetry (OTEL). This application was designed to be simple and easy to understand.\n\nThis application is how a traditional application would be instrumented. This application is based on gRPC platform that we have used before. We track more useful metrics to allow us to detect problems with our application.\n\nWe have removed most standard logging mechansisms (and no use of zero logger), instead we provide our own log package that writes our log message into the current span. That allows us to narrow our logging noise and avoid all the correlation required if you use standard logging. We only log startup information that is relevant before out service is ready (what ports we ran on, we can't start OTEL, startup args, ...).\n\nWe have spun off our own error package that logs our errors to the span and generates the error type.  This means we don't have to figure out when to log errors, they are always logged to spans. We only use standard errors when the error is not being generated in a span path.\n\nWe have moved our metrics and tracing constructors to their own packages and out of the main package. This lets us offer multiple places to put our traces or metrics. In the case of tracing, we offer a stderr tracing provider or provider that traces to a file.\n\nFinally, we provide our own tracing sampler which wraps one of the standard samplers. This allows us to trace whenever an RPC has the \"trace\" key in the gRPC request metadata or we receive one with a TraceID set. Otherwise we can do sampling at some rate, for ever RPC or not trace at all. Our sampler can be dialed up or down and this can be down with a management RPC we provide to allow changing our sampling.\n\n## Running\n\n- `docker-compose up -d` (if you remove -d, you will see all the logs from the docker jobs in stdout, ^c to make it stop)\n- Once started the client application will periodically add pets to the server until it runs out of names to add\n- Metrics will be collected for various things\n- Traces happen at 10% sampling rate\n- You can use the cli/petstore application to query the service yourself\n\nA sample query for all felines that have birthday's after Jan 1, 2004:\n\n`machine:.../client/petstore$ go run petstore.go search types=\"PTFeline\" birthdayStart='{\"month\":1, \"day\":1, \"year\":2004}'\n\nIf you want to force the query to do a trace, you can add `--trace` after `petstore.go`.\n\nPrometheus has metrics at: http://localhost:9090\nTraces are in Jaegar at: http://localhost:16686\n\n\nIf you see something like:\n```bash\ndocker-compose up -d\nTraceback (most recent call last):\n  File \"urllib3/connectionpool.py\", line 670, in urlopen\n  File \"urllib3/connectionpool.py\", line 392, in _make_request\n```\nThis indicates that you aren't running docker. Make sure you have docker installed and it is running. \n\n## Teardown\n\nSimple run; `docker-compose down`\n\n## Structure\n\nHere is a breakdown of how the petstore application is made. This is not required for the chapter's use, but looking at the code will give you a deeper insight into how you might want to structure a real application.\n\n```\n├── client\n│   ├── cli\n│   │   └── petstore\n│   │       └── petstore.go\n│   └── client.go\n├── internal\n│   └── server\n│       ├── errors\n│       ├── log\n│       ├── server.go\n│       ├── storage\n│       │   ├── mem\n│       │   └── storage.go\n│       └── telemetry\n│           ├── metrics\n│           └── tracing\n│               ├── sampler\n│               └── tracing.go\n├── petstore.go\n└── proto\n```\n\n* client/ Has an RPC client for the service\n* client/cli/petstore Is a CLI client to send RPCs to the petstore\n* petstore/ Is the main package\n* internal/server Is the gRPC service implementation\n* internal/server/errors The app's error package, works similar to the \"errors\" package from stdlib\n* internal/server/log The app's logging pacakge, similar to \"log\" from the stdlib\n* storage/ Defines the storage abstraction for the service\n* storage/mem Defines an in-memory storage implementation of storage.Data\n* telemetry/metrics Defines all the OpenTelemetry(OTEL) metrics for the application\n* telemetry/tracing Defines the Opentelemetry(OTEL) tracing for the application\n* proto/ Contains our protocol buffer definitions and Go packages\n\nOf note:\n\t* The log package outputs log messages to a tracing Span, not to a log file\n\t* If you do need to output some startup info, log.Logger is the standard log.Logger type, defaults to the default Logger\n\t* The errors package is a drop in replacement for \"log\", except Errorf() and New() take a Context\n\t* errors will write the error out to the current Span\n\n\n"
  },
  {
    "path": "chapter/11/petstore/client/cli/petstore/petstore.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/client\"\n\t\"google.golang.org/protobuf/encoding/protojson\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/proto\"\n\tdpb \"google.golang.org/genproto/googleapis/type/date\"\n)\n\nvar addr = flag.String(\"addr\", \"127.0.0.1:6742\", \"The host:port to connect to\")\n\nconst helpText = `\nPetstore CLI Client Help\n\nCommand Add:\n\tAdds pets to the petstore and returns a list of IDs.\n\n\tSyntax:\n\t\tpetstore add [pet description in JSON]\n\tExample:\n\t\tpetstore add '{\"name\":\"Stevie Nicks\", \"type\":\"PTFeline\", \"birthday\": {\"month\": 6, \"day\": 1, \"year\": 2005}}'\n\nCommand Delete:\n\tDeletes pets from the petstore.\n\n\tSyntax:\n\t\tpetstore delete [id] [id] [id] ...\n\tExample:\n\t\tpetstore delete 62809742-2de1-4208-a8cc-df485c48c563 83968fb4-9502-4df1-8680-7691fc1d3abe\n\nCommand Search:\n\tSearches for pets in the petstore.\n\n\tSyntax:\n\t\tpetstore search param=\"value\" parame=\"value\"\n\tParams:\n\t\tNames - Comma separated list of names\n\t\tType - Comma separated list of pet types\t\n\t\tBirthdayStart - JSON version of proto date\n\t\tBirthdayEnd - JSON version of proto date\n\n\t\tNote: \n\t\tIf BirthdayStart is provided by not BirthdayEnd, it will\n\t\tbe set to the current date + 1 day. If the reverse,\n\t\tBirthdayStart will be set to the Go's zero time.\n\t\t      \n\tExample:\n\t\tpetstore search names=\"Stevie Nicks, Frank\" types=\"PTFeline\" birthdayStart='{\"month\":1, \"day\":1, \"year\":2004}'\n`\n\nfunc main() {\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\n\tif len(os.Args) < 2 {\n\t\tfmt.Println(\"Error: arguments are not valid\")\n\t\tfmt.Println(helpText)\n\t\tos.Exit(1)\n\t}\n\n\tc, err := client.New(*addr)\n\tif err != nil {\n\t\tfmt.Printf(\"Error: problem connecting to server: %s\\n\", err)\n\t\tos.Exit(1)\n\t}\n\n\tcmd := strings.ToLower(os.Args[1])\n\tswitch cmd {\n\tcase \"add\":\n\t\tif len(os.Args) < 3 {\n\t\t\tfmt.Println(\"Error: not enough arguments to add command\")\n\t\t\tfmt.Println(helpText)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tp := &pb.Pet{}\n\t\tj := os.Args[2]\n\t\tif err := protojson.Unmarshal([]byte(j), p); err != nil {\n\t\t\tfmt.Printf(\"Error: problem with your pet description: %s\\n\", err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tids, err := c.AddPets(ctx, []*pb.Pet{p})\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"Error: problem adding your pets: %s\\n\", err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tfmt.Printf(\"%s was added as %s\\n\", p.Name, ids[0])\n\t\treturn\n\tcase \"delete\":\n\t\tif len(os.Args) < 3 {\n\t\t\tfmt.Println(\"Error: not enough arguments to delete command\")\n\t\t\tfmt.Println(helpText)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tif err := c.DeletePets(ctx, os.Args[2:]); err != nil {\n\t\t\tfmt.Printf(\"Error: problem deleting: %s\\n\", err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tfmt.Println(\"Delete succeeded\")\n\t\treturn\n\tcase \"search\":\n\t\tif len(os.Args) < 3 {\n\t\t\tfmt.Println(\"Error: not enough arguments to search command\")\n\t\t\tfmt.Println(helpText)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tr := getSearchReq()\n\t\tch, err := c.SearchPets(ctx, r)\n\t\tif err != nil {\n\t\t\tfmt.Println(err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tfor p := range ch {\n\t\t\tif p.Error() != nil {\n\t\t\t\tfmt.Println(p.Error())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\tfmt.Println(protojson.Format(p))\n\t\t}\n\tcase \"help\":\n\t\tfmt.Println(helpText)\n\tdefault:\n\t\tfmt.Println(\"Error: unknown command: \", cmd)\n\t\tfmt.Println(helpText)\n\t}\n}\n\nfunc getSearchReq() *pb.SearchPetsReq {\n\targsSeen := map[string]bool{\n\t\t\"names\":         false,\n\t\t\"types\":         false,\n\t\t\"birthdayStart\": false,\n\t\t\"birthdayEnd\":   false,\n\t}\n\n\tr := &pb.SearchPetsReq{}\n\n\tfor _, arg := range os.Args[2:] {\n\t\tswitch {\n\t\tcase strings.HasPrefix(arg, \"names\"):\n\t\t\tif argsSeen[\"names\"] {\n\t\t\t\tfmt.Println(\"cannot have multiple 'names' parameters\")\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\tsp := strings.Split(arg, \"=\")\n\t\t\tif len(sp) != 2 {\n\t\t\t\tfmt.Println(\"names parameter is malformed\")\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\tnames := strings.Trim(sp[1], `\"`)\n\t\t\tsp = strings.Split(names, \",\")\n\t\t\tfor _, name := range sp {\n\t\t\t\tr.Names = append(r.Names, strings.TrimSpace(name))\n\t\t\t}\n\t\t\targsSeen[\"names\"] = true\n\t\tcase strings.HasPrefix(arg, \"types\"):\n\t\t\tif argsSeen[\"types\"] {\n\t\t\t\tfmt.Println(\"cannot have multiple 'types' parameters\")\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\tsp := strings.Split(arg, \"=\")\n\t\t\tif len(sp) != 2 {\n\t\t\t\tfmt.Println(\"types parameter is malformed\")\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\ttypes := strings.Trim(sp[1], `\"`)\n\t\t\tsp = strings.Split(types, \",\")\n\t\t\tfor _, t := range sp {\n\t\t\t\tt = strings.TrimSpace(t)\n\t\t\t\te, ok := pb.PetType_value[t]\n\t\t\t\tif !ok {\n\t\t\t\t\tfmt.Printf(\"types parameter had value %q that we do not recognize\\n\", t)\n\t\t\t\t}\n\t\t\t\tr.Types = append(r.Types, pb.PetType(e))\n\t\t\t}\n\t\t\targsSeen[\"types\"] = true\n\t\tcase strings.HasPrefix(arg, \"birthdayStart\"):\n\t\t\tif argsSeen[\"birthdayStart\"] {\n\t\t\t\tfmt.Println(\"cannot have multiple 'birthdayStart' parameters\")\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\tsp := strings.Split(arg, \"=\")\n\t\t\tif len(sp) != 2 {\n\t\t\t\tfmt.Println(\"birthdayStart parameter is malformed\")\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\tstart := strings.Trim(sp[1], `\"`)\n\t\t\td := &dpb.Date{}\n\t\t\tif err := protojson.Unmarshal([]byte(start), d); err != nil {\n\t\t\t\tfmt.Printf(\"birthdayStart parameter is malformed: %s\\n\", err)\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\tif r.BirthdateRange == nil {\n\t\t\t\tr.BirthdateRange = &pb.DateRange{\n\t\t\t\t\tStart: d,\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tr.BirthdateRange.Start = d\n\t\t\t}\n\t\t\targsSeen[\"birthdayStart\"] = true\n\t\tcase strings.HasPrefix(arg, \"birthdayEnd\"):\n\t\t\tif argsSeen[\"birthdayEnd\"] {\n\t\t\t\tfmt.Println(\"cannot have multiple 'birthdayEnd' parameters\")\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\tsp := strings.Split(arg, \"=\")\n\t\t\tif len(sp) != 2 {\n\t\t\t\tfmt.Println(\"birthdayEnd parameter is malformed\")\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\tend := strings.Trim(sp[1], `\"`)\n\t\t\td := &dpb.Date{}\n\t\t\tif err := protojson.Unmarshal([]byte(end), d); err != nil {\n\t\t\t\tfmt.Printf(\"birthdayEnd parameter is malformed: %s\\n\", err)\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\tif r.BirthdateRange == nil {\n\t\t\t\tr.BirthdateRange = &pb.DateRange{\n\t\t\t\t\tEnd: d,\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tr.BirthdateRange.End = d\n\t\t\t}\n\t\t\targsSeen[\"birthdayEnd\"] = true\n\t\tdefault:\n\t\t\tfmt.Printf(\"parameter %q is one we don't support\\n\", arg)\n\t\t\tos.Exit(1)\n\t\t}\n\n\t\t// If one of the birthday ranges was put in (start or end) but not the other, add the other.\n\t\tif r.BirthdateRange != nil {\n\t\t\trng := r.BirthdateRange\n\t\t\tif rng.Start != nil && rng.End == nil {\n\t\t\t\tt := time.Now().Add(24 * time.Hour)\n\t\t\t\trng.End = &dpb.Date{Month: int32(t.Month()), Day: int32(t.Day()), Year: int32(t.Year())}\n\t\t\t}\n\t\t\tif rng.End != nil && rng.Start == nil {\n\t\t\t\tt := time.Time{}\n\t\t\t\trng.Start = &dpb.Date{Month: int32(t.Month()), Day: int32(t.Day()), Year: int32(t.Year())}\n\t\t\t}\n\t\t}\n\t}\n\treturn r\n}\n"
  },
  {
    "path": "chapter/11/petstore/client/client.go",
    "content": "// Client provides an API client to the petstore service.\npackage client\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/internal/server/storage\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/metadata\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/proto\"\n)\n\n// Client is a client to the petstore service.\ntype Client struct {\n\tclient pb.PetStoreClient\n\tconn   *grpc.ClientConn\n}\n\n// New is the constructor for Client. addr is the server's [host]:[port].\nfunc New(addr string) (*Client, error) {\n\tconn, err := grpc.Dial(addr, grpc.WithInsecure())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Client{\n\t\tclient: pb.NewPetStoreClient(conn),\n\t\tconn:   conn,\n\t}, nil\n}\n\n// Pet is a wrapper around a *pb.Pet that can return Go versions of\n// fields and errors if the returned stream has an error.\ntype Pet struct {\n\t*pb.Pet\n\terr error\n}\n\n// Proto will give the Pet's proto representation.\nfunc (p Pet) Proto() *pb.Pet {\n\treturn p.Pet\n}\n\n// Birthday returns the Pet's birthday as a time.Time.\nfunc (p Pet) Birthday() time.Time {\n\t// We are ignoring the error as we will either get a zero time\n\t// anyways and the server should be preventing this problem.\n\tt, _ := storage.BirthdayToTime(context.Background(), p.Pet.Birthday)\n\treturn t\n}\n\n// Error indicates if there was an error in the Pet output stream.\nfunc (p Pet) Error() error {\n\treturn p.err\n}\n\n// CallOptions are optional options for an RPC call.\ntype CallOption func(co *callOptions)\n\ntype callOptions struct {\n\ttrace *string\n}\n\n// TraceID will cause the RPC call to execute a trace on the service and return \"s\" to the ID.\n// If s == nil, this will ignore the option. If \"s\" is not set after the call finishes, then\n// no trace was made.\nfunc TraceID(s *string) CallOption {\n\treturn func(co *callOptions) {\n\t\tif s == nil {\n\t\t\treturn\n\t\t}\n\t\tco.trace = s\n\t}\n}\n\n// AddPets adds pets to the service and returns their unique identities in the\n// same order as being added.\nfunc (c *Client) AddPets(ctx context.Context, pets []*pb.Pet, options ...CallOption) ([]string, error) {\n\tif len(pets) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tfor _, p := range pets {\n\t\tif err := storage.ValidatePet(ctx, p, false); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tvar header metadata.MD\n\tctx, gOpts, f := handleCallOptions(ctx, &header, options)\n\tdefer f()\n\n\tresp, err := c.client.AddPets(ctx, &pb.AddPetsReq{Pets: pets}, gOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn resp.Ids, nil\n}\n\n// UpdatePets updates pets that already exist in the system.\nfunc (c *Client) UpdatePets(ctx context.Context, pets []*pb.Pet, options ...CallOption) error {\n\tif len(pets) == 0 {\n\t\treturn nil\n\t}\n\n\tfor _, p := range pets {\n\t\tif err := storage.ValidatePet(ctx, p, true); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tvar header metadata.MD\n\tctx, gOpts, f := handleCallOptions(ctx, &header, options)\n\tdefer f()\n\n\t_, err := c.client.UpdatePets(ctx, &pb.UpdatePetsReq{Pets: pets}, gOpts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// DeletePets deletes pets with the IDs passed. If the ID doesn't exist, the\n// system ignores it.\nfunc (c *Client) DeletePets(ctx context.Context, ids []string, options ...CallOption) error {\n\tif len(ids) == 0 {\n\t\treturn nil\n\t}\n\n\tvar header metadata.MD\n\tctx, gOpts, f := handleCallOptions(ctx, &header, options)\n\tdefer f()\n\n\t_, err := c.client.DeletePets(ctx, &pb.DeletePetsReq{Ids: ids}, gOpts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// SearchPets searches the pet store for pets matching the filter. If the filter contains\n// no entries, then all pets will be returned.\nfunc (c *Client) SearchPets(ctx context.Context, filter *pb.SearchPetsReq, options ...CallOption) (chan Pet, error) {\n\tif filter == nil {\n\t\treturn nil, fmt.Errorf(\"the filter cannot be nil\")\n\t}\n\n\tvar header metadata.MD\n\tctx, gOpts, f := handleCallOptions(ctx, &header, options)\n\n\tstream, err := c.client.SearchPets(ctx, filter, gOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tch := make(chan Pet, 1)\n\tgo func() {\n\t\tdefer close(ch)\n\t\tdefer f()\n\n\t\tfor {\n\t\t\tp, err := stream.Recv()\n\t\t\tif err == io.EOF {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\tch <- Pet{err: err}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tch <- Pet{Pet: p}\n\t\t}\n\t}()\n\treturn ch, nil\n}\n\n// SamplerType is the type of OTEL sampling to do.\ntype SamplerType int32\n\nconst (\n\tSTUnknown SamplerType = 0\n\tNever     SamplerType = 1\n\tAlways    SamplerType = 2\n\tFloat     SamplerType = 3\n)\n\nvar validTypes = map[SamplerType]bool{\n\tNever:  true,\n\tAlways: true,\n\tFloat:  true,\n}\n\ntype Sampler struct {\n\t// Type is the type of sampling to use.\n\tType SamplerType\n\t// Rate is the sampling rate, only used if type is Float.\n\tRate float64\n}\n\nfunc (s *Sampler) validate() error {\n\tif !validTypes[s.Type] {\n\t\treturn fmt.Errorf(\"type %v is not a supported type\", s.Type)\n\t}\n\tif s.Type == Float {\n\t\tif s.Rate <= 0 || s.Rate > 1 {\n\t\t\treturn fmt.Errorf(\"Rate must be > 0 && <= 1.0, was %v\", s.Rate)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (s *Sampler) proto() *pb.Sampler {\n\treturn &pb.Sampler{\n\t\tType:       pb.SamplerType(s.Type),\n\t\tFloatValue: s.Rate,\n\t}\n}\n\nfunc (s *Sampler) fromProto(p *pb.Sampler) {\n\ts.Type = SamplerType(p.Type)\n\ts.Rate = p.FloatValue\n}\n\n// ChangeSampler changes the sampling type and rate on the server. This is\n// and admin function that in production should be restricted.\nfunc (c *Client) ChangeSampler(ctx context.Context, sc Sampler, options ...CallOption) error {\n\tif err := sc.validate(); err != nil {\n\t\treturn err\n\t}\n\n\tvar header metadata.MD\n\tctx, gOpts, f := handleCallOptions(ctx, &header, options)\n\tdefer f()\n\n\t_, err := c.client.ChangeSampler(ctx, &pb.ChangeSamplerReq{Sampler: sc.proto()}, gOpts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc handleCallOptions(ctx context.Context, header *metadata.MD, options []CallOption) (context.Context, []grpc.CallOption, func()) {\n\topts := callOptions{}\n\tfor _, o := range options {\n\t\to(&opts)\n\t}\n\tvar gOpts []grpc.CallOption\n\n\tif opts.trace != nil {\n\t\t(*header)[\"trace\"] = nil\n\t\tgOpts = append(gOpts, grpc.Header(header))\n\t}\n\n\tf := func() {\n\t\tif opts.trace != nil {\n\t\t\tif len((*header)[\"otel.traceID\"]) != 0 {\n\t\t\t\t*opts.trace = (*header)[\"otel.traceID\"][0]\n\t\t\t}\n\t\t}\n\t}\n\n\treturn ctx, gOpts, f\n}\n"
  },
  {
    "path": "chapter/11/petstore/client/demo/Dockerfile",
    "content": "FROM golang:1.17\nCOPY . /usr/src/demo/\nWORKDIR /usr/src/demo/\nRUN go env -w GOPROXY=direct GO111MODULE=on\nWORKDIR /usr/src/demo/client/demo\nRUN go install\nCMD [\"/go/bin/demo\"]\n"
  },
  {
    "path": "chapter/11/petstore/client/demo/demo.go",
    "content": "/*\nThe demo will take a list of pet names and insert them into the Petstore every 1/2\na second. At the same time, starting 10 seconds after starting to add pets, another\ngoroutine will start and begin random searching for pets in the name file.\nIt will do this len(names) times. Because this is random, sometimes this will be\nan error (because the pet hasn't been added yet) and sometimes a success.\nThe longer this goes on, the less likely there will be an error.\n*/\npackage main\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\t\"log\"\n\t\"math/rand\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/client\"\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/proto\"\n\tdpb \"google.golang.org/genproto/googleapis/type/date\"\n)\n\n//go:embed names.txt\nvar namesFile string\n\nfunc main() {\n\tctx := context.Background()\n\n\ttime.Sleep(1 * time.Second)\n\tc, err := client.New(\"petstore:6742\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tnames := strings.Split(namesFile, \"\\n\")\n\n\twg := sync.WaitGroup{}\n\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\taddNames(ctx, c, names)\n\t}()\n\n\ttime.Sleep(10 * time.Second)\n\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tsearchNames(ctx, c, names)\n\t}()\n\n\twg.Wait()\n}\n\nfunc addNames(ctx context.Context, c *client.Client, names []string) {\n\tstart := time.Date(2020, 1, 1, 1, 1, 0, 0, time.UTC)\n\tt := 1\n\n\tfor _, name := range names {\n\t\tif strings.TrimSpace(name) == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tids, err := c.AddPets(\n\t\t\tctx,\n\t\t\t[]*pb.Pet{\n\t\t\t\t{\n\t\t\t\t\tName: name,\n\t\t\t\t\tType: pb.PetType(t),\n\t\t\t\t\tBirthday: &dpb.Date{\n\t\t\t\t\t\tMonth: int32(start.Month()),\n\t\t\t\t\t\tDay:   int32(start.Day()),\n\t\t\t\t\t\tYear:  int32(start.Year()),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t)\n\t\tif err != nil {\n\t\t\tpanic(\"had an unexpected problem: \" + err.Error())\n\t\t}\n\t\tlog.Println(\"Added pet with ID: \", ids[0])\n\t\tt++\n\t\t// Only 4 pet types.\n\t\tif t == 5 {\n\t\t\tt = 1\n\t\t}\n\t\tstart.Add(24 * time.Hour)\n\t\ttime.Sleep(500 * time.Millisecond)\n\t}\n}\n\nfunc searchNames(ctx context.Context, c *client.Client, names []string) {\n\tr := rand.New(rand.NewSource(time.Now().UnixNano()))\n\tl := len(names)\n\n\tfor i := 0; i < len(names); i++ {\n\t\tx := r.Intn(l)\n\t\tch, err := c.SearchPets(ctx, &pb.SearchPetsReq{Names: []string{names[x]}})\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"Search(%s): bad error: %s\", names[x], err)\n\t\t}\n\t\tvar results []client.Pet\n\t\tfor result := range ch {\n\t\t\tresults = append(results, result)\n\t\t}\n\t\tif len(results) > 0 {\n\t\t\tlog.Printf(\"Search(%s): found\", names[x])\n\t\t} else {\n\t\t\tlog.Printf(\"Search(%s): pet not found\", names[x])\n\t\t}\n\t\ttime.Sleep(1 * time.Second)\n\t}\n}\n"
  },
  {
    "path": "chapter/11/petstore/client/demo/names.txt",
    "content": "Abigail\nAce\nAdam\nAddie\nAdmiral\nAggie\nAires\nAj\nAjax\nAldo\nAlex\nAlexus\nAlf\nAlfie\nAllie\nAlly\nAmber\nAmie\nAmigo\nAmos\nAmy\nAndy\nAngel\nAngus\nAnnie\nApollo\nApril\nArchie\nArgus\nAries\nArmanti\nArnie\nArrow\nAshes\nAshley\nAstro\nAthena\nAtlas\nAudi\nAugie\nAussie\nAustin\nAutumn\nAxel\nAxle\nBabbles\nBabe\nBaby\nBaby-doll\nBabykins\nBacchus\nBailey\nBam-bam\nBambi\nBandit\nBanjo\nBarbie\nBarclay\nBarker\nBarkley\nBarley\nBarnaby\nBarney\nBaron\nBart\nBasil\nBaxter\nBb\nBeamer\nBeanie\nBeans\nBear\nBeau\nBeauty\nBeaux\nBebe\nBeetle\nBella\nBelle\nBen\nBenji\nBenny\nBenson\nBentley\nBernie\nBessie\nBiablo\nBibbles\nBig Boy\nBig Foot\nBiggie\nBillie\nBilly\nBingo\nBinky\nBirdie\nBirdy\nBiscuit\nBishop\nGus\nGuy\nGypsy\nHailey\nHaley\nHallie\nHamlet\nHammer\nHank\nHanna\nHannah\nHans\nHappy\nHardy\nHarley\nHarpo\nHarrison\nHarry\nHarvey\nHeather\nHeidi\nHenry\nHercules\nHershey\nHiggins\nHobbes\nHolly\nHomer\nHoney\nHoney-Bear\nHooch\nHoover\nHope\nHoudini\nHowie\nHudson\nHuey\nHugh\nHugo\nHumphrey\nHunter\nIndia\nIndy\nIris\nIsabella\nIsabelle\nItsy\nItsy-bitsy\nIvory\nIvy\nIzzy\nJack\nJackie\nJackpot\nJackson\nJade\nJagger\nJags\nJaguar\nJake\nJamie\nJasmine\nJasper\nJaxson\nJazmie\nJazz\nJelly\nJelly-bean\nJenna\nJenny\nJerry\nJersey\nJess\nJesse\nJesse James\nJessie\nJester\nJet\nJethro\nJett\nJetta\nJewel\nJewels\nJimmy\nJingles\nJJ\nJoe\nJoey\nJohnny\nJojo\nJoker\nJolie\nJolly\nJordan\nJosie\nJoy\nJR\nJudy\nJulius\nJune\nMisty\nMitch\nMittens\nMitzi\nMitzy\nMo\nMocha\nMollie\nMolly\nMona\nMuffy\nNakita\nNala\nNana\nNatasha\nNellie\nNemo\nNena\nPeanut\nPeanuts\nPearl\nPebbles\nPenny\nPhoebe\nPhoenix\nSara\nSarah\nSasha\nSassie\nSassy\nSavannah\nScarlett\nShasta\nSheba\nSheena\nShelby\nShelly\nSienna\nSierra\nSilky\nSilver\nSimone\nSissy\nSkeeter\nSky\nSkye\nSkyler\nWaldo\nWallace\nWally\nWalter\nWayne\nWeaver\nWebster\nWesley\nWestie\n"
  },
  {
    "path": "chapter/11/petstore/docker-compose.yaml",
    "content": "version: \"2\"\nservices:\n\n  # Jaeger\n  jaeger-all-in-one:\n    image: jaegertracing/all-in-one:latest\n    ports:\n      - \"16686:16686\"\n      - \"14268\"\n      - \"14250\"\n\n  # Collector\n  otel-collector:\n    image: ${OTELCOL_IMG}\n    command: [\"--config=/etc/otel-collector-config.yaml\", \"${OTELCOL_ARGS}\"]\n    volumes:\n      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml\n    ports:\n      - \"1888:1888\"   # pprof extension\n      - \"8888:8888\"   # Prometheus metrics exposed by the collector\n      - \"8889:8889\"   # Prometheus exporter metrics\n      - \"13133:13133\" # health_check extension\n      - \"4317\"        # OTLP gRPC receiver\n      - \"55670:55679\" # zpages extension\n    depends_on:\n      - jaeger-all-in-one\n\n  demo-client:\n    build:\n      dockerfile: ./client/demo/Dockerfile\n      context: ./\n    environment:\n      - OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317\n    depends_on:\n      - demo-server\n\n  demo-server:\n    build:\n      dockerfile: Dockerfile\n      context: ./\n    environment:\n      - OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317\n    ports:\n      - \"6742:6742\"\n    depends_on:\n      - otel-collector\n\n  prometheus:\n    container_name: prometheus\n    image: prom/prometheus:latest\n    volumes:\n      - ./prometheus.yaml:/etc/prometheus/prometheus.yml\n    ports:\n      - \"9090:9090\"\n"
  },
  {
    "path": "chapter/11/petstore/go.mod",
    "content": "module github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore\n\ngo 1.18\n\nrequire (\n\tgithub.com/biogo/store v0.0.0-20201120204734-aad293a2328f\n\tgithub.com/google/uuid v1.3.0\n\tgithub.com/kylelemons/godebug v1.1.0\n\tgo.opentelemetry.io/otel v1.4.1\n\tgo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.27.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.27.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.1\n\tgo.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.4.1\n\tgo.opentelemetry.io/otel/metric v0.27.0\n\tgo.opentelemetry.io/otel/sdk v1.4.1\n\tgo.opentelemetry.io/otel/sdk/metric v0.27.0\n\tgo.opentelemetry.io/otel/trace v1.4.1\n\tgoogle.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb\n\tgoogle.golang.org/grpc v1.45.0\n\tgoogle.golang.org/protobuf v1.28.0\n)\n\nrequire (\n\tgithub.com/cenkalti/backoff/v4 v4.1.2 // indirect\n\tgithub.com/go-logr/logr v1.2.3 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgithub.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.6.0 // indirect\n\tgo.opentelemetry.io/otel/internal/metric v0.27.0 // indirect\n\tgo.opentelemetry.io/proto/otlp v0.12.0 // indirect\n\tgolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect\n\tgolang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect\n\tgolang.org/x/text v0.3.5 // indirect\n)\n"
  },
  {
    "path": "chapter/11/petstore/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=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=\ngithub.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/biogo/store v0.0.0-20201120204734-aad293a2328f h1:+6okTAeUsUrdQr/qN7fIODzowrjjCrnJDg/gkYqcSXY=\ngithub.com/biogo/store v0.0.0-20201120204734-aad293a2328f/go.mod h1:z52shMwD6SGwRg2iYFjjDwX5Ene4ENTw6HfXraUy/08=\ngithub.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=\ngithub.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\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/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\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.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=\ngithub.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=\ngithub.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/protobuf v1.2.0/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.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/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.5.0/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.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=\ngithub.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=\ngithub.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=\ngithub.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=\ngithub.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=\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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.opentelemetry.io/otel v1.4.0/go.mod h1:jeAqMFKy2uLIxCtKxoFj0FAL5zAPKQagc3+GtBWakzk=\ngo.opentelemetry.io/otel v1.4.1 h1:QbINgGDDcoQUoMJa2mMaWno49lja9sHwp6aoa2n3a4g=\ngo.opentelemetry.io/otel v1.4.1/go.mod h1:StM6F/0fSwpd8dKWDCdRr7uRvEPYdW0hBSlbdTiUde4=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.1/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.6.0 h1:XFcfoo+vwXXwopiS7vzwbaFuPplf5GB+WTjaiQXmz3U=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.6.0/go.mod h1:NEu79Xo32iVb+0gVNV8PMd7GoWqnyDXRlj04yFjqz40=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.27.0 h1:t1aPfMj5oZzv2EaRmdC2QPQg1a7MaBjraOh4Hjwuia8=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.27.0/go.mod h1:aZnoYVx7GIuMROciGC3cjZhYxMD/lKroRJUnFY0afu0=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.27.0 h1:RJURCSrqUjJiCY3GuFCVP2EPKOQLwNXQ4FI3aH2KoHg=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.27.0/go.mod h1:LIc1eCpkU94tPnXxH40ya41Oyxm7sL+oDvxCYPFpnV8=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1 h1:WPpPsAAs8I2rA47v5u0558meKmmwm1Dj99ZbqCV8sZ8=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1/go.mod h1:o5RW5o2pKpJLD5dNTCmjF1DorYwMeFJmb/rKr5sLaa8=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.1 h1:AxqDiGk8CorEXStMDZF5Hz9vo9Z7ZZ+I5m8JRl/ko40=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.1/go.mod h1:c6E4V3/U+miqjs/8l950wggHGL1qzlp0Ypj9xoGrPqo=\ngo.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.4.1 h1:yaXaoJjXaJqRnsfW9HrN7pGb7bzcEn31Rk6yo2LFaWo=\ngo.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.4.1/go.mod h1:BFiGsTMZdqtxufux8ANXuMeRz9dMPVFdJZadUWDFD7o=\ngo.opentelemetry.io/otel/internal/metric v0.27.0 h1:9dAVGAfFiiEq5NVB9FUJ5et+btbDQAUIJehJ+ikyryk=\ngo.opentelemetry.io/otel/internal/metric v0.27.0/go.mod h1:n1CVxRqKqYZtqyTh9U/onvKapPGv7y/rpyOTI+LFNzw=\ngo.opentelemetry.io/otel/metric v0.27.0 h1:HhJPsGhJoKRSegPQILFbODU56NS/L1UE4fS1sC5kIwQ=\ngo.opentelemetry.io/otel/metric v0.27.0/go.mod h1:raXDJ7uP2/Jc0nVZWQjJtzoyssOYWu/+pjZqRzfvZ7g=\ngo.opentelemetry.io/otel/sdk v1.4.0/go.mod h1:71GJPNJh4Qju6zJuYl1CrYtXbrgfau/M9UAggqiy1UE=\ngo.opentelemetry.io/otel/sdk v1.4.1 h1:J7EaW71E0v87qflB4cDolaqq3AcujGrtyIPGQoZOB0Y=\ngo.opentelemetry.io/otel/sdk v1.4.1/go.mod h1:NBwHDgDIBYjwK2WNu1OPgsIc2IJzmBXNnvIJxJc8BpE=\ngo.opentelemetry.io/otel/sdk/metric v0.27.0 h1:CDEu96Js5IP7f4bJ8eimxF09V5hKYmE7CeyKSjmAL1s=\ngo.opentelemetry.io/otel/sdk/metric v0.27.0/go.mod h1:lOgrT5C3ORdbqp2LsDrx+pBj6gbZtQ5Omk27vH3EaW0=\ngo.opentelemetry.io/otel/trace v1.4.0/go.mod h1:uc3eRsqDfWs9R7b92xbQbU42/eTNz4N+gLP8qJCi4aE=\ngo.opentelemetry.io/otel/trace v1.4.1 h1:O+16qcdTrT7zxv2J6GejTPFinSwA++cYerC5iSiF8EQ=\ngo.opentelemetry.io/otel/trace v1.4.1/go.mod h1:iYEVbroFCNut9QkwEczV9vMRPHNKSSwYZjulEtsmhFc=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.opentelemetry.io/proto/otlp v0.12.0 h1:CMJ/3Wp7iOWES+CYLfnBv+DVmPbB+kmy9PJ92XvlR6c=\ngo.opentelemetry.io/proto/otlp v0.12.0/go.mod h1:TsIjwGWIx5VFYv9KGVlOpxoBl5Dy+63SUguV7GGvlSQ=\ngo.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=\ngo.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\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-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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=\ngolang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\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-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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\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=\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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb h1:0m9wktIpOxGw+SSKmydXWB3Z3GTfcPP6+q75HCQa6HI=\ngoogle.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=\ngoogle.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=\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.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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=\ngoogle.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\n"
  },
  {
    "path": "chapter/11/petstore/internal/server/errors/errors.go",
    "content": "// Package errors is a replacement for the golang standard library \"errors\". This replacement\n// adds errors to the Open Telemetry spans. The signatures only differs in that\n// New() now takes a context.Context object and fmt.Errorf() has been moved here and also takes a Context.Context.\npackage errors\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"go.opentelemetry.io/otel/codes\"\n\t\"go.opentelemetry.io/otel/trace\"\n)\n\n// New creates a new error and writes the error to a span if it exists in the context.\nfunc New(ctx context.Context, text string) error {\n\tspan := trace.SpanFromContext(ctx)\n\n\terr := errors.New(text)\n\tspan.RecordError(err)\n\tspan.SetStatus(codes.Error, err.Error())\n\n\treturn err\n}\n\n// Errorf implements fmt.Errorf with the addition of a Context that if it contains a span\n// will have the error added to the span.\nfunc Errorf(ctx context.Context, s string, i ...interface{}) error {\n\tspan := trace.SpanFromContext(ctx)\n\n\terr := fmt.Errorf(s, i...)\n\tspan.RecordError(err)\n\tspan.SetStatus(codes.Error, err.Error())\n\n\treturn err\n}\n\n// As implements errors.As().\nfunc As(err error, target interface{}) bool {\n\treturn errors.As(err, target)\n}\n\n// Is implements errors.Is().\nfunc Is(err, target error) bool {\n\treturn errors.Is(err, target)\n}\n\n// Unwrap implemements errors.Unwrap().\nfunc Unwrap(err error) error {\n\treturn errors.Unwrap(err)\n}\n"
  },
  {
    "path": "chapter/11/petstore/internal/server/log/log.go",
    "content": "/*\nPackage log is a replacement for the standard library log package that\nlogs to OTEL spans contained in Context objects. These are seen as\nevents with the attribute \"log\" set to true.\n\nThe preferred way to log is to use an event:\n\tfunc someFunc(ctx context.Context) {\n\t\te := NewEvent(\"someFunc()\")\n\t\tdefer e.Done(ctx)\n\t\tstart := time.Now()\n\t\tdefer func() {\n\t\t\te.Add(\"latency.ns\", int(time.Since(start)))\n\t\t}()\n\t}\n\nThis records an event in the current span that has a key of \"latency.ns\" with the value in nano-seconds the operation took.\n\nYou can use this to log in a similar manner to the logging package with Println and Printf.  This is generally only useful for some generic debugging where you want to log something and filter the trace by messages with key \"log\". Generally these are messages you don't want to keep.\n\tfunc main() {\n\t\tctx := context.Background()\n\n\t\tlog.SetFlags(log.LstdFlags | log.Lshortfile)\n\n\t\tlog.Println(ctx, \"Starting main\")\n\n\t\tlog.Printf(ctx, \"Env variables: %v\", os.Environ())\n\t}\n\nThe above won't log anything, as there is no Span on the Context. If there\nwas it would get output to the Open Telementry provider.\n\nIf you need to use the standard library log, you can use Logger:\n\tlog.Logger.Println(\"hello world\")\n\nThis would print whever the stanard logger prints to. This defaults\nto the standard logger, but you can replace with another Logger if you wish.\n\nYou should only log messages with a standard logger when it can't be output to a trace. These are critical messages that indicate a definite bug. This keeps logging to only critical events and de-clutters what you need to look at to when doing a debug.\n*/\npackage log\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"runtime\"\n\t\"sync\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n)\n\nconst (\n\tLdate         = 1 << iota     // the date in the local time zone: 2009/01/23\n\tLtime                         // the time in the local time zone: 01:23:23\n\tLmicroseconds                 // microsecond resolution: 01:23:23.123123.  assumes Ltime.\n\tLlongfile                     // full file name and line number: /a/b/c/d.go:23\n\tLshortfile                    // final file name element and line number: d.go:23. overrides Llongfile\n\tLUTC                          // if Ldate or Ltime is set, use UTC rather than the local time zone\n\tLstdFlags     = Ldate | Ltime // initial values for the standard logger\n)\n\n// Logger provides access to the standard library's default logger.\n// This can be replaced in main with a logger of your choice.\nvar Logger *log.Logger = log.Default()\n\n// setup the standard logger with flags.\nvar std = &logger{flag: LstdFlags}\n\n// pool provides a pool of Event objects to keep our allocations to a minimum.\nvar pool = &eventPool{\n\tbuf: make(chan *Event, 100),\n\tpool: sync.Pool{\n\t\tNew: func() interface{} {\n\t\t\treturn &Event{}\n\t\t},\n\t},\n}\n\n// eventPool uses a set amount of Event objects and a sync.Pool for overflow.\n// Note: this would actually be a great place for metrics to key in on what would be\n// an optimal size for buf to prevent pool use.\ntype eventPool struct {\n\tbuf  chan *Event\n\tpool sync.Pool\n}\n\nfunc (e *eventPool) get() *Event {\n\tselect {\n\tcase ev := <-e.buf:\n\t\treturn ev\n\tdefault:\n\t}\n\treturn e.pool.Get().(*Event)\n}\n\nfunc (e *eventPool) put(ev *Event) {\n\tev.reset()\n\tselect {\n\tcase e.buf <- ev:\n\tdefault:\n\t}\n\te.pool.Put(ev)\n}\n\n// Event represents a named event that occurs. This is the prefered way to log data.\n// Events have attributes and those attributes are key/value pairs. You create\n// an event and stuff attributes using Add() until the event is over and call Done().\n// This will render the event to the current span. if no attrs exist, the event is ignored.\n// To avoid extra allocations\ntype Event struct {\n\tname  string\n\tattrs []attribute.KeyValue\n}\n\n// NewEvent returns a new Event.\nfunc NewEvent(name string) *Event {\n\tev := pool.get()\n\tev.name = name\n\treturn ev\n}\n\nfunc (e *Event) reset() {\n\te.name = \"\"\n\te.attrs = e.attrs[0:0]\n}\n\n// Add adds an attribute named k with value i. i can be: bool, []bool, float64, []float64, int, []int, int64, []int64, string and []string.\n// If the value isn't one of those values, a standard log message is printed indicating a bug.\nfunc (e *Event) Add(k string, i interface{}) {\n\tif e.name == \"\" {\n\t\treturn\n\t}\n\tswitch v := i.(type) {\n\tcase bool:\n\t\te.attrs = append(e.attrs, attribute.Bool(k, v))\n\tcase []bool:\n\t\te.attrs = append(e.attrs, attribute.BoolSlice(k, v))\n\tcase float64:\n\t\te.attrs = append(e.attrs, attribute.Float64(k, v))\n\tcase []float64:\n\t\te.attrs = append(e.attrs, attribute.Float64Slice(k, v))\n\tcase int:\n\t\te.attrs = append(e.attrs, attribute.Int(k, v))\n\tcase []int:\n\t\te.attrs = append(e.attrs, attribute.IntSlice(k, v))\n\tcase int64:\n\t\te.attrs = append(e.attrs, attribute.Int64(k, v))\n\tcase []int64:\n\t\te.attrs = append(e.attrs, attribute.Int64Slice(k, v))\n\tcase string:\n\t\te.attrs = append(e.attrs, attribute.String(k, v))\n\tcase []string:\n\t\te.attrs = append(e.attrs, attribute.StringSlice(k, v))\n\tcase time.Duration:\n\t\te.attrs = append(e.attrs, attribute.String(k, v.String()))\n\tdefault:\n\t\tlog.Printf(\"bug: event.Add(): receiveing %T which is not supported\", v)\n\t}\n}\n\n// Done renders the Event to the span in the Context. If there are no attributes on the Event, this is a no-oop.\n// Once Done is called, the Event object MUST not be used again.\nfunc (e *Event) Done(ctx context.Context) {\n\tdefer pool.put(e)\n\n\tif e.name == \"\" {\n\t\treturn\n\t}\n\tspan := trace.SpanFromContext(ctx)\n\tif e.attrs == nil {\n\t\treturn\n\t}\n\tspan.AddEvent(e.name, trace.WithAttributes(e.attrs...))\n}\n\n// Println acts like log.Println() except we log to the OTEL span in the Context.\nfunc Println(ctx context.Context, v ...interface{}) {\n\tspan := trace.SpanFromContext(ctx)\n\tif !span.IsRecording() {\n\t\treturn\n\t}\n\tstd.output(span, 2, fmt.Sprintln(v...))\n}\n\n// Printf acts like log.Printf() except we log to the OTEL span in the Context.\nfunc Printf(ctx context.Context, format string, v ...interface{}) {\n\tspan := trace.SpanFromContext(ctx)\n\tif !span.IsRecording() {\n\t\treturn\n\t}\n\tstd.output(span, 2, fmt.Sprintf(format, v...))\n}\n\n// SetFlags sets the output flags for the standard logger.\nfunc SetFlags(flag int) {\n\tstd.flag = flag\n}\n\n// logger is an implementation of log.Logger that writes to a Span.\ntype logger struct {\n\tmu   sync.Mutex\n\tflag int    // properties\n\tbuf  []byte // for accumulating text to write\n}\n\n// Cheap integer to fixed-width decimal ASCII. Give a negative width to avoid zero-padding.\nfunc itoa(buf *[]byte, i int, wid int) {\n\t// Assemble decimal in reverse order.\n\tvar b [20]byte\n\tbp := len(b) - 1\n\tfor i >= 10 || wid > 1 {\n\t\twid--\n\t\tq := i / 10\n\t\tb[bp] = byte('0' + i - q*10)\n\t\tbp--\n\t\ti = q\n\t}\n\t// i < 10\n\tb[bp] = byte('0' + i)\n\t*buf = append(*buf, b[bp:]...)\n}\n\nfunc (l *logger) output(span trace.Span, calldepth int, s string) error {\n\tnow := time.Now() // get this early\n\tvar file string\n\tvar line int\n\tl.mu.Lock()\n\tdefer l.mu.Unlock()\n\n\tif l.flag&(Lshortfile|Llongfile) != 0 {\n\t\t// Release lock while getting caller info - it's expensive.\n\t\tl.mu.Unlock()\n\t\tvar ok bool\n\t\t_, file, line, ok = runtime.Caller(calldepth)\n\t\tif !ok {\n\t\t\tfile = \"???\"\n\t\t\tline = 0\n\t\t}\n\t\tl.mu.Lock()\n\t}\n\tl.buf = l.buf[:0]\n\tl.formatHeader(&l.buf, now, file, line)\n\tl.buf = append(l.buf, s...)\n\tif len(s) == 0 || s[len(s)-1] != '\\n' {\n\t\tl.buf = append(l.buf, '\\n')\n\t}\n\tspan.AddEvent(string(l.buf), trace.WithAttributes(attribute.Bool(\"log\", true)))\n\treturn nil\n}\n\n// formatHeader writes log header to buf in following order:\n//   * date and/or time (if corresponding flags are provided),\n//   * file and line number (if corresponding flags are provided),\nfunc (l *logger) formatHeader(buf *[]byte, t time.Time, file string, line int) {\n\tif l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {\n\t\tif l.flag&LUTC != 0 {\n\t\t\tt = t.UTC()\n\t\t}\n\t\tif l.flag&Ldate != 0 {\n\t\t\tyear, month, day := t.Date()\n\t\t\titoa(buf, year, 4)\n\t\t\t*buf = append(*buf, '/')\n\t\t\titoa(buf, int(month), 2)\n\t\t\t*buf = append(*buf, '/')\n\t\t\titoa(buf, day, 2)\n\t\t\t*buf = append(*buf, ' ')\n\t\t}\n\t\tif l.flag&(Ltime|Lmicroseconds) != 0 {\n\t\t\thour, min, sec := t.Clock()\n\t\t\titoa(buf, hour, 2)\n\t\t\t*buf = append(*buf, ':')\n\t\t\titoa(buf, min, 2)\n\t\t\t*buf = append(*buf, ':')\n\t\t\titoa(buf, sec, 2)\n\t\t\tif l.flag&Lmicroseconds != 0 {\n\t\t\t\t*buf = append(*buf, '.')\n\t\t\t\titoa(buf, t.Nanosecond()/1e3, 6)\n\t\t\t}\n\t\t\t*buf = append(*buf, ' ')\n\t\t}\n\t}\n\tif l.flag&(Lshortfile|Llongfile) != 0 {\n\t\tif l.flag&Lshortfile != 0 {\n\t\t\tshort := file\n\t\t\tfor i := len(file) - 1; i > 0; i-- {\n\t\t\t\tif file[i] == '/' {\n\t\t\t\t\tshort = file[i+1:]\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tfile = short\n\t\t}\n\t\t*buf = append(*buf, file...)\n\t\t*buf = append(*buf, ':')\n\t\titoa(buf, line, -1)\n\t\t*buf = append(*buf, \": \"...)\n\t}\n}\n"
  },
  {
    "path": "chapter/11/petstore/internal/server/server.go",
    "content": "// Package server contains our gRPC server implementation for the pet store.\npackage server\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/internal/server/errors\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/internal/server/storage\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/internal/server/telemetry/metrics\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/internal/server/telemetry/tracing\"\n\n\t\"github.com/google/uuid\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\totelCodes \"go.opentelemetry.io/otel/codes\"\n\t\"go.opentelemetry.io/otel/metric\"\n\tsdkTrace \"go.opentelemetry.io/otel/sdk/trace\"\n\t\"go.opentelemetry.io/otel/trace\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/metadata\"\n\t\"google.golang.org/grpc/peer\"\n\t\"google.golang.org/grpc/reflection\"\n\t\"google.golang.org/grpc/status\"\n\t\"google.golang.org/protobuf/encoding/protojson\"\n\t\"google.golang.org/protobuf/proto\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/proto\"\n)\n\n// These represent all of our OTEL metric counters.\nvar (\n\ttotalCount, addCount, deleteCount, updateCount, searchCount metric.Int64Counter\n\n\taddCurrent, deleteCurrent, updateCurrent, searchCurrent metric.Int64UpDownCounter\n\n\taddLat, deleteLat, updateLat, searchLat metric.Int64Histogram\n\n\taddErrors, deleteErrors, updateErrors, searchErrors metric.Int64Counter\n)\n\n// This fetches all of our counters. You can only do this in init().\nfunc init() {\n\ttotalCount = metrics.Get.Int64(\"petstore/server/totals/requests\")\n\taddCount = metrics.Get.Int64(\"petstore/server/AddPets/requests\")\n\tdeleteCount = metrics.Get.Int64(\"petstore/server/DeletePets/requests\")\n\tupdateCount = metrics.Get.Int64(\"petstore/server/UpdatePets/requests\")\n\tsearchCount = metrics.Get.Int64(\"petstore/server/SearchPets/requests\")\n\n\taddCurrent = metrics.Get.Int64UD(\"petstore/server/AddPets/current\")\n\tdeleteCurrent = metrics.Get.Int64UD(\"petstore/server/DeletePets/current\")\n\tupdateCurrent = metrics.Get.Int64UD(\"petstore/server/UpdatePets/current\")\n\tsearchCurrent = metrics.Get.Int64UD(\"petstore/server/SearchPets/current\")\n\n\taddErrors = metrics.Get.Int64(\"petstore/server/AddPets/errors\")\n\tdeleteErrors = metrics.Get.Int64(\"petstore/server/DeletePets/errors\")\n\tupdateErrors = metrics.Get.Int64(\"petstore/server/UpdatePets/errors\")\n\tsearchErrors = metrics.Get.Int64(\"petstore/server/SearchPets/errors\")\n\n\taddLat = metrics.Get.Int64Hist(\"petstore/server/AddPets/latency\")\n\tdeleteLat = metrics.Get.Int64Hist(\"petstore/server/DeletePets/latency\")\n\tupdateLat = metrics.Get.Int64Hist(\"petstore/server/UpdatePets/latency\")\n\tsearchLat = metrics.Get.Int64Hist(\"petstore/server/SearchPets/latency\")\n}\n\n// API implements our gRPC server's API.\ntype API struct {\n\tpb.UnimplementedPetStoreServer\n\n\taddr  string\n\tstore storage.Data\n\n\tgrpcServer *grpc.Server\n\tgOpts      []grpc.ServerOption\n\tmu         sync.Mutex\n}\n\n// Option is an optional arguments to New().\ntype Option func(a *API)\n\n// WithGRPCOpts creates the gRPC server with the options passed.\nfunc WithGRPCOpts(opts ...grpc.ServerOption) Option {\n\treturn func(a *API) {\n\t\ta.gOpts = append(a.gOpts, opts...)\n\t}\n}\n\n// New is the constructore for API.\nfunc New(addr string, store storage.Data, options ...Option) (*API, error) {\n\ta := &API{addr: addr, store: store}\n\n\tfor _, o := range options {\n\t\to(a)\n\t}\n\n\ta.grpcServer = grpc.NewServer(a.gOpts...)\n\ta.grpcServer.RegisterService(&pb.PetStore_ServiceDesc, a)\n\treflection.Register(a.grpcServer)\n\n\treturn a, nil\n}\n\n// Start starts the server. This blocks until Stop() is called.\nfunc (a *API) Start() error {\n\ta.mu.Lock()\n\tdefer a.mu.Unlock()\n\n\tlis, err := net.Listen(\"tcp\", a.addr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn a.grpcServer.Serve(lis)\n}\n\n// Stop stops the server.\nfunc (a *API) Stop() {\n\ta.mu.Lock()\n\tdefer a.mu.Unlock()\n\n\ta.grpcServer.Stop()\n}\n\n// AddPets adds pets to the pet store.\nfunc (a *API) AddPets(ctx context.Context, req *pb.AddPetsReq) (resp *pb.AddPetsResp, err error) {\n\t// Handle tracing.\n\tctx, _, end := doTrace(ctx, \"server.AddPets()\", req)\n\tdefer func() { end(err) }()\n\n\t// Handle metrics.\n\tmetrics.Meter.RecordBatch(\n\t\tctx,\n\t\tnil,\n\t\ttotalCount.Measurement(1),\n\t\taddCount.Measurement(1),\n\t\taddCurrent.Measurement(1),\n\t)\n\tt := time.Now()\n\tdefer func() {\n\t\tmetrics.Meter.RecordBatch(\n\t\t\tctx,\n\t\t\tnil,\n\t\t\taddCurrent.Measurement(-1),\n\t\t\taddLat.Measurement(int64(time.Since(t))),\n\t\t)\n\t\tif err != nil {\n\t\t\tcode := status.Code(err)\n\t\t\taddErrors.Add(ctx, 1, attribute.String(\"code\", code.String()))\n\t\t}\n\t}()\n\n\t// Actual work.\n\tids := make([]string, 0, len(req.Pets))\n\tfor _, p := range req.Pets {\n\t\tif err := storage.ValidatePet(ctx, p, false); err != nil {\n\t\t\treturn nil, status.Error(codes.InvalidArgument, err.Error())\n\t\t}\n\t\tp.Id = uuid.New().String()\n\t\tids = append(ids, p.Id)\n\t}\n\n\tif err = a.store.AddPets(ctx, req.Pets); err != nil {\n\t\treturn nil, status.Error(codes.Internal, err.Error())\n\t}\n\treturn &pb.AddPetsResp{Ids: ids}, nil\n}\n\n// UpdatePets updates pets in the pet store.\nfunc (a *API) UpdatePets(ctx context.Context, req *pb.UpdatePetsReq) (resp *pb.UpdatePetsResp, err error) {\n\tctx, _, end := doTrace(ctx, \"server.UpdatePets()\", req)\n\tdefer func() { end(err) }()\n\n\t// Handle metrics.\n\tmetrics.Meter.RecordBatch(\n\t\tctx,\n\t\tnil,\n\t\ttotalCount.Measurement(1),\n\t\tupdateCount.Measurement(1),\n\t\tupdateCurrent.Measurement(1),\n\t)\n\tt := time.Now()\n\tdefer func() {\n\t\tmetrics.Meter.RecordBatch(\n\t\t\tctx,\n\t\t\tnil,\n\t\t\tupdateCurrent.Measurement(-1),\n\t\t\tupdateLat.Measurement(int64(time.Since(t))),\n\t\t)\n\t\tif err != nil {\n\t\t\tcode := status.Code(err)\n\t\t\tupdateErrors.Add(ctx, 1, attribute.String(\"code\", code.String()))\n\t\t}\n\t}()\n\n\tfor _, p := range req.Pets {\n\t\tif err = storage.ValidatePet(ctx, p, true); err != nil {\n\t\t\treturn nil, status.Error(codes.InvalidArgument, err.Error())\n\t\t}\n\t}\n\n\tif err = a.store.UpdatePets(ctx, req.Pets); err != nil {\n\t\treturn nil, status.Error(codes.Internal, err.Error())\n\t}\n\treturn &pb.UpdatePetsResp{}, nil\n}\n\n// DeletePets deletes pets from the pet store.\nfunc (a *API) DeletePets(ctx context.Context, req *pb.DeletePetsReq) (resp *pb.DeletePetsResp, err error) {\n\tctx, _, end := doTrace(ctx, \"server.DeletePets()\", req)\n\tdefer func() { end(err) }()\n\n\t// Handle metrics.\n\tmetrics.Meter.RecordBatch(\n\t\tctx,\n\t\tnil,\n\t\ttotalCount.Measurement(1),\n\t\tdeleteCount.Measurement(1),\n\t\tdeleteCurrent.Measurement(1),\n\t)\n\tt := time.Now()\n\tdefer func() {\n\t\tmetrics.Meter.RecordBatch(\n\t\t\tctx,\n\t\t\tnil,\n\t\t\tdeleteCurrent.Measurement(-1),\n\t\t\tdeleteLat.Measurement(int64(time.Since(t))),\n\t\t)\n\t\tif err != nil {\n\t\t\tcode := status.Code(err)\n\t\t\tdeleteErrors.Add(ctx, 1, attribute.String(\"code\", code.String()))\n\t\t}\n\t}()\n\n\tif err = a.store.DeletePets(ctx, req.Ids); err != nil {\n\t\treturn nil, status.Error(codes.Internal, err.Error())\n\t}\n\treturn &pb.DeletePetsResp{}, nil\n}\n\n// SearchPets finds pets in the pet store.\nfunc (a *API) SearchPets(req *pb.SearchPetsReq, stream pb.PetStore_SearchPetsServer) (err error) {\n\tcount := 0\n\n\tctx, span, end := doTrace(stream.Context(), \"server.SearchPets()\", req)\n\tdefer func() { end(err) }()\n\tdefer func() {\n\t\tspan.SetAttributes(attribute.Int(\"search.results.returned\", count))\n\t}()\n\n\t// Handle metrics.\n\tmetrics.Meter.RecordBatch(\n\t\tctx,\n\t\tnil,\n\t\ttotalCount.Measurement(1),\n\t\tsearchCount.Measurement(1),\n\t\tsearchCurrent.Measurement(1),\n\t)\n\tt := time.Now()\n\tdefer func() {\n\t\tmetrics.Meter.RecordBatch(\n\t\t\tctx,\n\t\t\tnil,\n\t\t\tsearchCurrent.Measurement(-1),\n\t\t\tsearchLat.Measurement(int64(time.Since(t))),\n\t\t)\n\t\tif err != nil {\n\t\t\tcode := status.Code(err)\n\t\t\tsearchErrors.Add(ctx, 1, attribute.String(\"code\", code.String()))\n\t\t}\n\t}()\n\n\tif err = validateSearch(ctx, req); err != nil {\n\t\treturn status.Error(codes.InvalidArgument, err.Error())\n\t}\n\n\tch := a.store.SearchPets(ctx, req)\n\tfor item := range ch {\n\t\tcount++\n\t\tif item.Error != nil {\n\t\t\treturn status.Error(codes.Internal, item.Error.Error())\n\t\t}\n\t\tif err := stream.Send(item.Pet); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif ctx.Err() != nil {\n\t\treturn status.Error(codes.DeadlineExceeded, stream.Context().Err().Error())\n\t}\n\treturn nil\n}\n\n// ChangeSampler changes the OTEL sampling type.\nfunc (a *API) ChangeSampler(ctx context.Context, req *pb.ChangeSamplerReq) (resp *pb.ChangeSamplerResp, err error) {\n\tswitch req.Sampler.Type {\n\tcase pb.SamplerType_STUnknown:\n\t\t// Skip, will return an error\n\tcase pb.SamplerType_STNever:\n\t\ttracing.Sampler.Switch(sdkTrace.NeverSample())\n\t\treturn &pb.ChangeSamplerResp{}, nil\n\tcase pb.SamplerType_STAlways:\n\t\ttracing.Sampler.Switch(sdkTrace.AlwaysSample())\n\t\treturn &pb.ChangeSamplerResp{}, nil\n\tcase pb.SamplerType_STFloat:\n\t\tif req.Sampler.FloatValue <= 0 || req.Sampler.FloatValue > 1 {\n\t\t\treturn nil, status.Error(codes.InvalidArgument, fmt.Sprintf(\"float_value=%v is invalid\", req.Sampler.FloatValue))\n\t\t}\n\t\ttracing.Sampler.Switch(sdkTrace.TraceIDRatioBased(req.Sampler.FloatValue))\n\t\treturn &pb.ChangeSamplerResp{}, nil\n\t}\n\treturn nil, status.Error(codes.InvalidArgument, fmt.Sprintf(\"type==%v is invalid\", req.Sampler.Type))\n}\n\nfunc validateSearch(ctx context.Context, r *pb.SearchPetsReq) error {\n\tfor _, t := range r.Types {\n\t\tif t == pb.PetType_PTUnknown {\n\t\t\treturn errors.New(ctx, \"cannot search for PetType_Unkonwn\")\n\t\t}\n\t}\n\n\tif r.BirthdateRange != nil {\n\t\tif r.BirthdateRange.Start == nil {\n\t\t\treturn errors.New(ctx, \"cannot have a BirthdateRange.Start that is nil\")\n\t\t}\n\t\tif r.BirthdateRange.End == nil {\n\t\t\treturn errors.New(ctx, \"cannot have a BirthdateRange.End that is nil\")\n\t\t}\n\t\tif _, err := storage.BirthdayToTime(ctx, r.BirthdateRange.Start); err != nil {\n\t\t\treturn errors.Errorf(ctx, \"r.BirthdateRange.Start had error: %s\", err)\n\t\t}\n\t\tif _, err := storage.BirthdayToTime(ctx, r.BirthdateRange.End); err != nil {\n\t\t\treturn errors.Errorf(ctx, \"r.BirthdateRange.End had error: %s\", err)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc doTrace(ctx context.Context, name string, req proto.Message) (newCtx context.Context, span trace.Span, end func(err error)) {\n\tctx, span = tracing.Tracer.Start(\n\t\tctx,\n\t\tname,\n\t\ttrace.WithAttributes(\n\t\t\tattribute.String(\"args\", protojson.Format(req)),\n\t\t\tattribute.Bool(\"grpcCall\", true),\n\t\t),\n\t)\n\tp, ok := peer.FromContext(ctx)\n\tif ok {\n\t\thost, port, err := net.SplitHostPort(p.Addr.String())\n\t\tif err == nil {\n\t\t\tportNum, _ := strconv.Atoi(port)\n\t\t\tspan.SetAttributes(\n\t\t\t\tattribute.String(\"net.peer.ip\", host),\n\t\t\t\tattribute.Int(\"net.peer.port\", portNum),\n\t\t\t)\n\t\t}\n\t}\n\n\t// If they asked for a trace, send back the trace ID.\n\tif ctx.Value(\"trace\") != nil {\n\t\tid := span.SpanContext().TraceID().String()\n\t\tif id != \"\" {\n\t\t\theader := metadata.Pairs(\"traceID\", convertTraceID(id))\n\t\t\tgrpc.SendHeader(ctx, header)\n\t\t}\n\t}\n\n\treturn ctx, span, func(err error) {\n\t\tif err != nil {\n\t\t\tspan.SetStatus(otelCodes.Error, err.Error())\n\t\t\tspan.SetAttributes(\n\t\t\t\tattribute.Bool(\"error\", true),\n\t\t\t\tattribute.String(\"errorMsg\", err.Error()),\n\t\t\t)\n\t\t\tspan.End()\n\t\t\treturn\n\t\t}\n\t\tspan.SetStatus(otelCodes.Ok, \"\")\n\t\tspan.End()\n\t}\n}\n\nfunc convertTraceID(id string) string {\n\tif len(id) < 16 {\n\t\treturn \"\"\n\t}\n\tif len(id) > 16 {\n\t\tid = id[16:]\n\t}\n\tintValue, err := strconv.ParseUint(id, 16, 64)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn strconv.FormatUint(intValue, 10)\n}\n"
  },
  {
    "path": "chapter/11/petstore/internal/server/storage/mem/mem.go",
    "content": "// Package mem contains an in-memory storage implementation of storage.Data.\n// This is great for unit tests and demos. Our implementation uses a\n// left-leaning red black tree for storage of entries by birthdays and maps\n// for all other indexes. Filtering is done by searching all indexes for matches\n// by each filter and if all matches succeed we stream the entry found.\npackage mem\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/internal/server/errors\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/internal/server/log\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/internal/server/storage\"\n\n\t\"github.com/biogo/store/llrb\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/proto\"\n)\n\n// birthdays represents a set of pets that share the same birthday with\n// keys that are pet IDs. This is what we insert into our birthday tree.\ntype birthdays map[string]*pb.Pet\n\n// Compare implements the llrb.Comparable.Compare().\nfunc (bi birthdays) Compare(b llrb.Comparable) int {\n\tvar ap, bp *pb.Pet\n\t// Get any entry in the map, all have the same birthday.\n\tfor _, ap = range bi {\n\t\tbreak\n\t}\n\tfor _, bp = range b.(birthdays) {\n\t\tbreak\n\t}\n\n\t// Ignore errors because we have to conform to a function def\n\t// and we should not be storing records with errors in the Birthday.\n\tat, _ := storage.BirthdayToTime(nil, ap.Birthday)\n\tbt, _ := storage.BirthdayToTime(nil, bp.Birthday)\n\n\tswitch {\n\tcase at.Before(bt):\n\t\treturn -1\n\tcase at.Equal(bt):\n\t\treturn 0\n\t}\n\treturn 1\n}\n\n// birthdayGet is what we use to search for a pets with a particular birthday.\ntype birthdayGet struct {\n\t*pb.Pet\n}\n\n// Compare implements the llrb.Comparable.Compare().\nfunc (bi birthdayGet) Compare(b llrb.Comparable) int {\n\t// Ignore errors because we have to conform to a function def\n\t// and we should not be storing records with errors in the Birthday.\n\tat, _ := storage.BirthdayToTime(nil, bi.Pet.Birthday)\n\tvar bt time.Time\n\tswitch v := b.(type) {\n\tcase birthdayGet:\n\t\tbt, _ = storage.BirthdayToTime(nil, v.Pet.Birthday)\n\tcase birthdays:\n\t\tvar p *pb.Pet\n\t\tfor _, p = range v {\n\t\t\tbreak\n\t\t}\n\t\tbt, _ = storage.BirthdayToTime(nil, p.Birthday)\n\t}\n\n\tswitch {\n\tcase at.Before(bt):\n\t\treturn -1\n\tcase at.Equal(bt):\n\t\treturn 0\n\t}\n\treturn 1\n}\n\n// Data implements storage.Data.\ntype Data struct {\n\tmu       sync.RWMutex // protects the items in this block\n\tbirthday *llrb.Tree\n\tnames    map[string]map[string]*pb.Pet\n\tids      map[string]*pb.Pet\n\ttypes    map[pb.PetType]map[string]*pb.Pet\n\n\t// searches contains all the search calls that must be done\n\t// when we do a search. This is populated in New().\n\tsearches []func(context.Context, *pb.SearchPetsReq) []string\n}\n\n// New is the constructor for Data.\nfunc New() *Data {\n\td := Data{\n\t\tnames:    map[string]map[string]*pb.Pet{},\n\t\tids:      map[string]*pb.Pet{},\n\t\tbirthday: &llrb.Tree{},\n\t\ttypes:    map[pb.PetType]map[string]*pb.Pet{},\n\t}\n\td.searches = []func(context.Context, *pb.SearchPetsReq) []string{\n\t\td.byNames,\n\t\td.byTypes,\n\t\td.byBirthdays,\n\t}\n\treturn &d\n}\n\n// AddPets implements storage.Data.AddPets().\nfunc (d *Data) AddPets(ctx context.Context, pets []*pb.Pet) error {\n\te := log.NewEvent(\"mem.data.AddPets()\")\n\tdefer e.Done(ctx)\n\tstart := time.Now()\n\tdefer func() {\n\t\te.Add(\"latency.ns\", time.Since(start))\n\t}()\n\n\td.mu.RLock()\n\t// Make sure that none of these IDs somehow exist already.\n\tfor _, p := range pets {\n\t\tif _, ok := d.ids[p.Id]; ok {\n\t\t\treturn errors.Errorf(ctx, \"pet with ID(%s) is already present\", p.Id)\n\t\t}\n\t}\n\td.mu.RUnlock()\n\n\td.mu.Lock()\n\tdefer d.mu.Unlock()\n\n\td.populate(ctx, pets)\n\treturn nil\n}\n\n// UpdatePets implements storage.Data.AddPets().\nfunc (d *Data) UpdatePets(ctx context.Context, pets []*pb.Pet) error {\n\td.mu.RLock()\n\t// Make sure that ALL of these IDs somehow exist already.\n\tfor _, p := range pets {\n\t\tif _, ok := d.ids[p.Id]; !ok {\n\t\t\treturn errors.Errorf(ctx, \"pet with ID(%s) doesn't exist\", p.Id)\n\t\t}\n\t}\n\td.mu.RUnlock()\n\n\td.mu.Lock()\n\tdefer d.mu.Unlock()\n\n\td.populate(ctx, pets)\n\treturn nil\n}\n\nfunc (d *Data) populate(ctx context.Context, pets []*pb.Pet) {\n\te := log.NewEvent(\"mem.data.populate()\")\n\tdefer e.Done(ctx)\n\n\tfor _, p := range pets {\n\t\td.ids[p.Id] = p\n\t\tif v, ok := d.names[p.Name]; ok {\n\t\t\tv[p.Id] = p\n\t\t} else {\n\t\t\td.names[p.Name] = map[string]*pb.Pet{\n\t\t\t\tp.Id: p,\n\t\t\t}\n\t\t}\n\t\tif v, ok := d.types[p.Type]; ok {\n\t\t\tv[p.Id] = p\n\t\t} else {\n\t\t\td.types[p.Type] = map[string]*pb.Pet{\n\t\t\t\tp.Id: p,\n\t\t\t}\n\t\t}\n\t\tv := d.birthday.Get(birthdayGet{p})\n\t\tif v == nil {\n\t\t\td.birthday.Insert(birthdays{p.Id: p})\n\t\t\tcontinue\n\t\t}\n\t\tv.(birthdays)[p.Id] = p\n\t}\n}\n\n// DeletePets implements stroage.Data.DeletePets().\nfunc (d *Data) DeletePets(ctx context.Context, ids []string) error {\n\te := log.NewEvent(\"mem.data.DeletePets()\")\n\tdefer e.Done(ctx)\n\tstart := time.Now()\n\tdefer func() {\n\t\te.Add(\"latency.ns\", time.Since(start))\n\t}()\n\n\td.mu.Lock()\n\tdefer d.mu.Unlock()\n\n\tfor _, id := range ids {\n\t\tp, ok := d.ids[id]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tdelete(d.ids, id)\n\t\tif v, ok := d.names[p.Name]; ok {\n\t\t\tif len(v) == 1 {\n\t\t\t\tdelete(d.names, p.Name)\n\t\t\t} else {\n\t\t\t\tdelete(v, id)\n\t\t\t}\n\t\t}\n\t\tif v, ok := d.types[p.Type]; ok {\n\t\t\tif len(v) == 1 {\n\t\t\t\tdelete(d.types, p.Type)\n\t\t\t} else {\n\t\t\t\tdelete(v, id)\n\t\t\t}\n\t\t}\n\t\tv := d.birthday.Get(birthdayGet{p})\n\t\tif v == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif len(v.(birthdays)) == 1 {\n\t\t\td.birthday.Delete(birthdayGet{p})\n\t\t}\n\t\tdelete(v.(birthdays), p.Id)\n\t}\n\treturn nil\n}\n\n// SearchPets implements storage.Data.SearchPets().\nfunc (d *Data) SearchPets(ctx context.Context, filter *pb.SearchPetsReq) chan storage.SearchItem {\n\tpetsCh := make(chan storage.SearchItem, 1)\n\n\tgo func() {\n\t\tdefer close(petsCh)\n\t\td.searchPets(ctx, filter, petsCh)\n\t}()\n\n\treturn petsCh\n}\n\nfunc (d *Data) searchPets(ctx context.Context, filter *pb.SearchPetsReq, out chan storage.SearchItem) {\n\te := log.NewEvent(\"mem.data.searchPets()\")\n\tdefer e.Done(ctx)\n\n\td.mu.RLock()\n\tdefer d.mu.RUnlock()\n\n\tfilters := 0\n\tif len(filter.Names) > 0 {\n\t\te.Add(\"filterNames\", true)\n\t\tfilters++\n\t}\n\tif len(filter.Types) > 0 {\n\t\te.Add(\"filterTypes\", true)\n\t\tfilters++\n\t}\n\tif filter.BirthdateRange != nil {\n\t\te.Add(\"filterBirthday\", true)\n\t\tfilters++\n\t}\n\n\t// They didn't provide filters, so just return everything.\n\tif filters == 0 {\n\t\te.Add(\"returnAll\", true)\n\t\td.returnAll(ctx, out)\n\t\treturn\n\t}\n\n\tsearchCh := make(chan []string, len(d.searches))\n\twg := sync.WaitGroup{}\n\twg.Add(len(d.searches))\n\n\tgoCount := 0\n\t// Spin off our searches.\n\tfor _, search := range d.searches {\n\t\tgoCount++\n\t\tsearch := search\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tr := search(ctx, filter)\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\tcase searchCh <- r:\n\t\t\t}\n\t\t}()\n\t}\n\te.Add(\"search.goroutines\", goCount)\n\n\t// Wait for our searches to complete then close our searchCh.\n\tgo func() { wg.Wait(); close(searchCh) }()\n\n\t// Collect all IDs from searches and count them. When one hits\n\t// the total number of filters send the matching pet to the caller.\n\tm := map[string]int{}\n\tmatchCh := make(chan string, 1)\n\tgo func() {\n\t\tdefer close(matchCh)\n\t\tfor ids := range searchCh {\n\t\t\tfor _, id := range ids {\n\t\t\t\tcount := m[id]\n\t\t\t\tcount++\n\t\t\t\tm[id] = count\n\t\t\t\tif count == filters {\n\t\t\t\t\tmatchCh <- id\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n\n\t// This handles all our matches getting returned.\n\tvalCount := 0\n\tlatency := 0\n\tdefer func() {\n\t\tif valCount > 0 {\n\t\t\te.Add(\"upstream.recv.latency.avg.ns\", latency/valCount)\n\t\t}\n\t}()\n\n\tfor {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\tcase id, ok := <-matchCh:\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tstart := time.Now()\n\t\t\tout <- storage.SearchItem{Pet: d.ids[id]}\n\t\t\tvalCount++\n\t\t\tlatency += int(time.Since(start))\n\t\t}\n\t}\n}\n\n// returnAll streams all the pets that we have.\nfunc (d *Data) returnAll(ctx context.Context, out chan storage.SearchItem) {\n\te := log.NewEvent(\"mem.data.returnAll()\")\n\tdefer e.Done(ctx)\n\n\tstart := time.Now()\n\tcount := 0\n\tdefer func() {\n\t\te.Add(\"latency.ns\", int(time.Since(start)))\n\t\te.Add(\"count\", count)\n\t}()\n\n\tfor _, p := range d.ids {\n\t\tcount++\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\tcase out <- storage.SearchItem{Pet: p}:\n\t\t}\n\t}\n}\n\n// byNames returns IDs of pets that have the names matched in the filter.\nfunc (d *Data) byNames(ctx context.Context, filter *pb.SearchPetsReq) []string {\n\tif len(filter.Names) == 0 {\n\t\treturn nil\n\t}\n\n\te := log.NewEvent(\"mem.data.byNames()\")\n\tdefer e.Done(ctx)\n\n\tstart := time.Now()\n\tcount := 0\n\tdefer func() {\n\t\te.Add(\"latency.ns\", int(time.Since(start)))\n\t\te.Add(\"count\", count)\n\t}()\n\tvar ids []string\n\tfor _, n := range filter.Names {\n\t\tcount++\n\t\tif ctx.Err() != nil {\n\t\t\treturn nil\n\t\t}\n\t\tp, ok := d.names[n]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tfor id := range p {\n\t\t\tids = append(ids, id)\n\t\t}\n\t}\n\treturn ids\n}\n\n// byTypes returns IDs of pets that have the types matched in the filter.\nfunc (d *Data) byTypes(ctx context.Context, filter *pb.SearchPetsReq) []string {\n\tif len(filter.Types) == 0 {\n\t\treturn nil\n\t}\n\te := log.NewEvent(\"mem.data.byTypes()\")\n\tdefer e.Done(ctx)\n\n\tstart := time.Now()\n\tcount := 0\n\tdefer func() {\n\t\te.Add(\"latency.ns\", int(time.Since(start)))\n\t\te.Add(\"count\", count)\n\t}()\n\tvar ids []string\n\tfor _, t := range filter.Types {\n\t\tcount++\n\t\tif ctx.Err() != nil {\n\t\t\treturn nil\n\t\t}\n\t\tp, ok := d.types[t]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tfor id := range p {\n\t\t\tids = append(ids, id)\n\t\t}\n\t}\n\treturn ids\n}\n\n// byBirthdays returns IDs of pets that have the birthdays matched in the filter.\nfunc (d *Data) byBirthdays(ctx context.Context, filter *pb.SearchPetsReq) []string {\n\tif filter.BirthdateRange == nil {\n\t\treturn nil\n\t}\n\n\te := log.NewEvent(\"mem.data.byBirthdays()\")\n\tdefer e.Done(ctx)\n\n\tstart := time.Now()\n\tcount := 0\n\tdefer func() {\n\t\te.Add(\"latency.ns\", int(time.Since(start)))\n\t\te.Add(\"count\", count)\n\t}()\n\n\tvar ids []string\n\td.birthday.DoRange(\n\t\tfunc(c llrb.Comparable) (done bool) {\n\t\t\tfor _, p := range c.(birthdays) {\n\t\t\t\tif ctx.Err() != nil {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tids = append(ids, p.Id)\n\t\t\t}\n\t\t\treturn\n\t\t},\n\t\tbirthdayGet{&pb.Pet{Birthday: filter.BirthdateRange.Start}},\n\t\tbirthdayGet{&pb.Pet{Birthday: filter.BirthdateRange.End}},\n\t)\n\tif ctx.Err() != nil {\n\t\treturn nil\n\t}\n\tcount = len(ids)\n\treturn ids\n}\n"
  },
  {
    "path": "chapter/11/petstore/internal/server/storage/mem/mem_test.go",
    "content": "package mem\n\nimport (\n\t\"context\"\n\t\"sort\"\n\t\"strconv\"\n\t\"testing\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/internal/server/storage\"\n\n\t\"github.com/kylelemons/godebug/pretty\"\n\t\"google.golang.org/protobuf/proto\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/proto\"\n\tdpb \"google.golang.org/genproto/googleapis/type/date\"\n)\n\n// This tests we implement the interface.\nvar _ storage.Data = &Data{}\n\nvar pets = []*pb.Pet{\n\t{\n\t\tId:       \"0\",\n\t\tName:     \"Adam\",\n\t\tType:     pb.PetType_PTCanine,\n\t\tBirthday: &dpb.Date{Month: 1, Day: 1, Year: 2020},\n\t},\n\t{\n\t\tId:       \"1\",\n\t\tName:     \"Becky\",\n\t\tType:     pb.PetType_PTFeline,\n\t\tBirthday: &dpb.Date{Month: 2, Day: 1, Year: 2020},\n\t},\n\t{\n\t\tId:       \"2\",\n\t\tName:     \"Calvin\",\n\t\tType:     pb.PetType_PTFeline,\n\t\tBirthday: &dpb.Date{Month: 2, Day: 2, Year: 2020},\n\t},\n\t{\n\t\tId:       \"3\",\n\t\tName:     \"David\",\n\t\tType:     pb.PetType_PTBird,\n\t\tBirthday: &dpb.Date{Month: 2, Day: 2, Year: 2021},\n\t},\n\t{\n\t\tId:       \"4\",\n\t\tName:     \"Elaine\",\n\t\tType:     pb.PetType_PTReptile,\n\t\tBirthday: &dpb.Date{Month: 2, Day: 2, Year: 2021},\n\t},\n\t{\n\t\tId:       \"5\",\n\t\tName:     \"Elaine\",\n\t\tType:     pb.PetType_PTReptile,\n\t\tBirthday: &dpb.Date{Month: 2, Day: 3, Year: 2021},\n\t},\n}\n\n// makePets takes the global \"pets\" var and clones everything in it and puts it into\n// a *Data so we have test data.\nfunc makePets() *Data {\n\td := New()\n\n\tn := []*pb.Pet{}\n\tfor _, p := range pets {\n\t\tn = append(n, proto.Clone(p).(*pb.Pet))\n\t}\n\n\td.AddPets(context.Background(), n)\n\treturn d\n}\n\nfunc TestByNames(t *testing.T) {\n\td := makePets()\n\n\tgot := d.byNames(context.Background(), &pb.SearchPetsReq{Names: []string{\"David\", \"Elaine\"}})\n\tsort.Strings(got)\n\n\twant := []string{\"3\", \"4\", \"5\"}\n\tif diff := pretty.Compare(want, got); diff != \"\" {\n\t\tt.Errorf(\"TestByNames: -want/+got:\\n%s\", diff)\n\t}\n}\n\nfunc TestByTypes(t *testing.T) {\n\td := makePets()\n\n\tgot := d.byTypes(context.Background(), &pb.SearchPetsReq{Types: []pb.PetType{pb.PetType_PTCanine, pb.PetType_PTReptile}})\n\tsort.Strings(got)\n\n\twant := []string{\"0\", \"4\", \"5\"}\n\tif diff := pretty.Compare(want, got); diff != \"\" {\n\t\tt.Errorf(\"TestByTypes: -want/+got:\\n%s\", diff)\n\t}\n}\n\nfunc TestByBirthdays(t *testing.T) {\n\td := makePets()\n\n\tgot := d.byBirthdays(\n\t\tcontext.Background(),\n\t\t&pb.SearchPetsReq{\n\t\t\tBirthdateRange: &pb.DateRange{\n\t\t\t\tStart: &dpb.Date{Month: 2, Day: 1, Year: 2020},\n\t\t\t\tEnd:   &dpb.Date{Month: 2, Day: 3, Year: 2021},\n\t\t\t},\n\t\t},\n\t)\n\tsort.Strings(got)\n\n\twant := []string{\"1\", \"2\", \"3\", \"4\"}\n\tif diff := pretty.Compare(want, got); diff != \"\" {\n\t\tt.Errorf(\"TestByBirthdays: -want/+got:\\n%s\", diff)\n\t}\n}\n\nfunc TestDeletePets(t *testing.T) {\n\td := makePets()\n\n\tdeletions := []string{\"3\", \"5\", \"20\"}\n\n\tif err := d.DeletePets(context.Background(), deletions); err != nil {\n\t\tt.Fatalf(\"TestDeletePets: got err == %v, want err == nil\", err)\n\t}\n\n\t// Don't check the last deletion, it is only there to make sure\n\t// a non-existent value doesn't do anything.\n\tfor _, id := range deletions[:len(deletions)-1] {\n\t\tif _, ok := d.ids[id]; ok {\n\t\t\tt.Errorf(\"TestDeletePets: found ids[%s]\", id)\n\t\t}\n\t\ti, _ := strconv.Atoi(id)\n\n\t\tif m, ok := d.names[pets[i].Name]; ok {\n\t\t\tif _, ok := m[id]; ok {\n\t\t\t\tt.Errorf(\"TestDeletePets: found(%s) in names\", id)\n\t\t\t}\n\t\t}\n\t\tif m, ok := d.types[pets[i].Type]; ok {\n\t\t\tif _, ok := m[id]; ok {\n\t\t\t\tt.Errorf(\"TestDeletePets: found(%s) in types\", id)\n\t\t\t}\n\t\t}\n\n\t\tv := d.birthday.Get(birthdayGet{pets[i]})\n\t\tif v != nil {\n\t\t\tif _, ok := v.(birthdays)[id]; ok {\n\t\t\t\tt.Errorf(\"TestDeletePets: found(%s) in birthday tree\", id)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestSearchPets(t *testing.T) {\n\td := makePets()\n\n\tch := d.SearchPets(\n\t\tcontext.Background(),\n\t\t&pb.SearchPetsReq{\n\t\t\tNames: []string{\n\t\t\t\t\"Becky\",\n\t\t\t\t\"Calvin\",\n\t\t\t\t\"David\",\n\t\t\t\t\"Elaine\",\n\t\t\t},\n\t\t\tTypes: []pb.PetType{\n\t\t\t\tpb.PetType_PTReptile,\n\t\t\t\tpb.PetType_PTFeline,\n\t\t\t},\n\t\t\tBirthdateRange: &pb.DateRange{\n\t\t\t\tStart: &dpb.Date{Month: 2, Day: 2, Year: 2021},\n\t\t\t\tEnd:   &dpb.Date{Month: 2, Day: 3, Year: 2021},\n\t\t\t},\n\t\t},\n\t)\n\n\tgot := []storage.SearchItem{}\n\tfor item := range ch {\n\t\tgot = append(got, item)\n\t}\n\n\twant := []storage.SearchItem{{Pet: pets[4]}}\n\n\tconfig := pretty.Config{TrackCycles: true}\n\tif diff := config.Compare(want, got); diff != \"\" {\n\t\tt.Errorf(\"TestSearchPets: -want/+got:\\n%s\", diff)\n\t}\n}\n"
  },
  {
    "path": "chapter/11/petstore/internal/server/storage/storage.go",
    "content": "package storage\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/internal/server/errors\"\n\n\tdpb \"google.golang.org/genproto/googleapis/type/date\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/proto\"\n)\n\n// Data represents our data storage.\ntype Data interface {\n\t// AddPets adds pet entries into storage.\n\tAddPets(ctx context.Context, pets []*pb.Pet) error\n\t// UpdatePets updates pet entries in storage.\n\tUpdatePets(ctx context.Context, pets []*pb.Pet) error\n\t// DeletePets deletes pets in storage by their ID. Will not error\n\t// on IDs not found.\n\tDeletePets(ctx context.Context, ids []string) error\n\t// SearchPets searches storage for pet entries that match the\n\t// filter.\n\tSearchPets(ctx context.Context, filter *pb.SearchPetsReq) chan SearchItem\n}\n\n// SearchItem is an item returned by a search.\ntype SearchItem struct {\n\t// Pet is the pet that matched the search filters.\n\tPet *pb.Pet\n\t// Error indicates that there was an error. If set the channel\n\t// will close after this entry.\n\tError error\n}\n\n// ValidatePet validates that *pb.Pet has valid fields.\nfunc ValidatePet(ctx context.Context, p *pb.Pet, forUpdate bool) error {\n\tif forUpdate && p.Id == \"\" {\n\t\treturn errors.New(ctx, \"updates must have the Id field set\")\n\t} else {\n\t\tif !forUpdate && p.Id != \"\" {\n\t\t\treturn errors.New(ctx, \"cannot set the Id field\")\n\t\t}\n\t}\n\tp.Name = strings.TrimSpace(p.Name)\n\tif p.Name == \"\" {\n\t\treturn errors.New(ctx, \"cannot have a pet without a name\")\n\t}\n\n\tif p.Type == pb.PetType_PTUnknown {\n\t\treturn errors.New(ctx, \"cannot have an unknown pet type\")\n\t}\n\n\t_, err := BirthdayToTime(ctx, p.Birthday)\n\tif err != nil {\n\t\treturn errors.Errorf(ctx, \"pet(%s) had an error in its birthday: %w\", p.Name, err)\n\t}\n\treturn nil\n\n}\n\n// BirthdayToTime converts the *pb.Pet.Birthday field to a time.Time object.\nfunc BirthdayToTime(ctx context.Context, d *dpb.Date) (time.Time, error) {\n\tif d.Month < 1 || d.Month > 12 {\n\t\treturn time.Time{}, errors.Errorf(ctx, \"month must be 1-12, was %d\", d.Month)\n\t}\n\tif d.Day < 1 || d.Day > 31 {\n\t\treturn time.Time{}, errors.Errorf(ctx, \"day(%d) was invalid\", d.Day)\n\t}\n\tt := time.Date(int(d.Year), time.Month(d.Month), int(d.Day), 0, 0, 0, 0, time.UTC)\n\n\tif t.Month() != time.Month(d.Month) {\n\t\treturn time.Time{}, errors.Errorf(ctx, \"month %v does not have %d days\", time.Month(d.Month), d.Day)\n\t}\n\treturn t, nil\n}\n"
  },
  {
    "path": "chapter/11/petstore/internal/server/telemetry/metrics/metrics.go",
    "content": "/*\nPackage metrics provides setup of metrics that can be used internally to measure various application states.\nAll metrics for the application are defined here and other applications use this package to grab the metrics\nand use them. This package will also report any metric that is not used in the first 10 seconds after the app has started\nto prevent useless metrics from existing, as all metrics should be grabbed by that time.\n\nIn a package you want to set metrics, you can do it as follows:\n\tvar addCount metrics.Int64Counter\n\n\tfunc init() {\n\t\taddCounter = metrics.Get.Int64(\"petstore/server/AddPets/requests\")\n\t}\n\t...\n\n\tfunc (s *Server) AddPets(ctx context.Context, req *pb.AddPetsReq) (*pb.AddpetsResp, error) {\n\t\t...\n\t\t// Do this if you have multiple changes that don't require special labels per update.\n\t\tmetrics.Meter.RecordBatch(ctx, nil, addCounter.Measure(ctx, 1))\n\t\t// Do this if you only need to make one change or need special labels.\n\t\taddCounter.Add(ctx, 1, attribute.String(\"label\", \"value\")\n\t\t...\n\t}\n\n\nTo cause metrics to be exported package main():\n\tfunc main() {\n\t\t...\n\t\tstop, err := metrics.Start(ctx, metrics.OTELGRPC{Addr: \"ip:port\"})\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\tdefer stop()\n\t\t...\n\t}\n*/\npackage metrics\n\nimport (\n\t\"html/template\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/internal/server/log\"\n\n\t\"go.opentelemetry.io/otel/metric\"\n\t\"go.opentelemetry.io/otel/metric/global\"\n)\n\ntype metricType int\n\nconst (\n\tunknown     = 0\n\tmtInt64     = 1\n\tmtInt64Hist = 2\n\tmtInt64UD   = 3\n)\n\ntype metricDef struct {\n\tmtype metricType\n\tname  string\n\tdesc  string\n}\n\nvar metrics = []metricDef{\n\t// Histograms\n\t{mtInt64Hist, \"petstore/server/AddPets/latency\", \"The latency of an AddPets() request in nanoseconds\"},\n\t{mtInt64Hist, \"petstore/server/DeletePets/latency\", \"The latency of an DeletePets() request in nanoseconds\"},\n\t{mtInt64Hist, \"petstore/server/UpdatePets/latency\", \"The latency of an UpdatePets() request in nanoseconds\"},\n\t{mtInt64Hist, \"petstore/server/SearchPets/latency\", \"The latency of a SearchPets() request in nanoseconds\"},\n\t// Counters\n\t{mtInt64, \"petstore/server/AddPets/requests\", \"The total requests made to AddPets()\"},\n\t{mtInt64, \"petstore/server/DeletePets/requests\", \"The total requests made to DeletePets()\"},\n\t{mtInt64, \"petstore/server/UpdatePets/requests\", \"The total requests made to UpdatePets()\"},\n\t{mtInt64, \"petstore/server/SearchPets/requests\", \"The total requests made to SearchPets()\"},\n\t{mtInt64, \"petstore/server/totals/requests\", \"The total requests made to the server\"},\n\n\t{mtInt64, \"petstore/server/AddPets/errors\", \"The total error count\"},\n\t{mtInt64, \"petstore/server/DeletePets/errors\", \"The total error couunt\"},\n\t{mtInt64, \"petstore/server/UpdatePets/errors\", \"The total error count\"},\n\t{mtInt64, \"petstore/server/SearchPets/errors\", \"The total error count\"},\n\t{mtInt64, \"petstore/server/totals/errors\", \"The total error count for all RPCs\"},\n\n\t// UpDown Counters\n\t{mtInt64UD, \"petstore/server/AddPets/current\", \"The amount of requests currently being proccessed\"},\n\t{mtInt64UD, \"petstore/server/DeletePets/current\", \"The amount of requests currently being proccessed\"},\n\t{mtInt64UD, \"petstore/server/UpdatePets/current\", \"The amount of requests currently being proccessed\"},\n\t{mtInt64UD, \"petstore/server/SearchPets/current\", \"The amount of requests currently being proccessed\"},\n}\n\n// Meter is the meter for the petstore.\nvar Meter = global.Meter(\"petstore\")\n\n// Get is used to lookup metrics by name.\nvar Get = newLookups()\n\nvar unusedMetricsTmpl = template.Must(\n\ttemplate.New(\"\").Parse(\n\t\t`\nThe following metrics appeart to be unused:\n{{- range .}}\n\t{{.}}\n{{- end }}\n`,\n\t),\n)\n\n// Lookups provides lookups for metrics based on their names.\ntype Lookups struct {\n\tmtInt64Hist map[string]metric.Int64Histogram\n\tmtInt64UD   map[string]metric.Int64UpDownCounter\n\tmtInt64     map[string]metric.Int64Counter\n\n\tmu   sync.Mutex\n\tused map[string]bool\n}\n\nfunc newLookups() *Lookups {\n\tl := &Lookups{\n\t\tmtInt64Hist: map[string]metric.Int64Histogram{},\n\t\tmtInt64:     map[string]metric.Int64Counter{},\n\t\tmtInt64UD:   map[string]metric.Int64UpDownCounter{},\n\t\tused:        map[string]bool{},\n\t}\n\n\texists := map[string]bool{}\n\tfor _, m := range metrics {\n\t\tif m.mtype == unknown {\n\t\t\tlog.Logger.Fatalf(\"metric with type(%v) cannot be added\", m.mtype)\n\t\t}\n\t\tif m.name == \"\" {\n\t\t\tlog.Logger.Fatalf(\"metric cannot be missing a name\")\n\t\t}\n\t\tif m.desc == \"\" {\n\t\t\tlog.Logger.Fatalf(\"metric cannot be missing a desc\")\n\t\t}\n\t\tif exists[m.name] {\n\t\t\tlog.Logger.Fatalf(\"cannot have two metrics with same name(%s)\", m.name)\n\t\t}\n\t\texists[m.name] = true\n\n\t\tswitch m.mtype {\n\t\tcase mtInt64Hist:\n\t\t\tl.mtInt64Hist[m.name] = metric.Must(Meter).NewInt64Histogram(m.name, metric.WithDescription(m.desc))\n\t\tcase mtInt64UD:\n\t\t\tl.mtInt64UD[m.name] = metric.Must(Meter).NewInt64UpDownCounter(m.name, metric.WithDescription(m.desc))\n\t\tcase mtInt64:\n\t\t\tl.mtInt64[m.name] = metric.Must(Meter).NewInt64Counter(m.name, metric.WithDescription(m.desc))\n\t\tdefault:\n\t\t\tlog.Logger.Fatalf(\"bug: we defined a metric type(%v) without adding support\", m.mtype)\n\t\t}\n\t}\n\tgo func() {\n\t\ttime.Sleep(10 * time.Second)\n\t\tunused := l.unused()\n\t\ts := strings.Builder{}\n\t\tif err := unusedMetricsTmpl.Execute(&s, unused); err != nil {\n\t\t\tlog.Logger.Fatalf(\"unusedMetricTmpl execute error: %s\", err)\n\t\t}\n\t\tlog.Logger.Println(s.String())\n\t}()\n\treturn l\n}\n\n// Int64 grabs the Int64Counter metric named \"s\". If not found, panics.\nfunc (l *Lookups) Int64(s string) metric.Int64Counter {\n\tl.mu.Lock()\n\tdefer l.mu.Unlock()\n\n\tm, ok := l.mtInt64[s]\n\tif !ok {\n\t\tlog.Logger.Fatalf(\"int64 metric(%s) is not defined\", s)\n\t}\n\tl.used[s] = true\n\treturn m\n}\n\n// Int64s grabs a list of Int64Counters.\nfunc (l *Lookups) Int64s(s ...string) []metric.Int64Counter {\n\tv := make([]metric.Int64Counter, 0, len(s))\n\tfor _, name := range s {\n\t\tv = append(v, l.Int64(name))\n\t}\n\treturn v\n}\n\n// Int64UD grabs the Int64UpDownCounter metric named \"s\". If not found, panics.\nfunc (l *Lookups) Int64UD(s string) metric.Int64UpDownCounter {\n\tl.mu.Lock()\n\tdefer l.mu.Unlock()\n\n\tm, ok := l.mtInt64UD[s]\n\tif !ok {\n\t\tlog.Logger.Fatalf(\"int64ud metric(%s) is not defined\", s)\n\t}\n\tl.used[s] = true\n\treturn m\n}\n\n// Int64UDs grabs a list of Int64UpDownCounters.\nfunc (l *Lookups) Int64UDs(s ...string) []metric.Int64UpDownCounter {\n\tv := make([]metric.Int64UpDownCounter, 0, len(s))\n\tfor _, name := range s {\n\t\tv = append(v, l.Int64UD(name))\n\t}\n\treturn v\n}\n\n// Int64Hist grabs the Int64Histogram metric named \"s\". If not found, panics.\nfunc (l *Lookups) Int64Hist(s string) metric.Int64Histogram {\n\tl.mu.Lock()\n\tdefer l.mu.Unlock()\n\n\tm, ok := l.mtInt64Hist[s]\n\tif !ok {\n\t\tlog.Logger.Fatalf(\"int64 histogram metric(%s) is not defined\", s)\n\t}\n\tl.used[s] = true\n\treturn m\n}\n\nfunc (l *Lookups) Int64Hists(s ...string) []metric.Int64Histogram {\n\tv := make([]metric.Int64Histogram, 0, len(s))\n\tfor _, name := range s {\n\t\tv = append(v, l.Int64Hist(name))\n\t}\n\treturn v\n}\n\nfunc (l *Lookups) unused() []string {\n\tl.mu.Lock()\n\tdefer l.mu.Unlock()\n\n\tunused := []string{}\n\tfor k := range l.mtInt64Hist {\n\t\tif !l.used[k] {\n\t\t\tunused = append(unused, k)\n\t\t}\n\t}\n\tfor k := range l.mtInt64 {\n\t\tif !l.used[k] {\n\t\t\tunused = append(unused, k)\n\t\t}\n\t}\n\tsort.Strings(unused)\n\treturn unused\n}\n"
  },
  {
    "path": "chapter/11/petstore/internal/server/telemetry/metrics/start.go",
    "content": "package metrics\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlpmetric\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc\"\n\t\"go.opentelemetry.io/otel/metric/global\"\n\t\"go.opentelemetry.io/otel/sdk/metric/controller/basic\"\n\tprocessor \"go.opentelemetry.io/otel/sdk/metric/processor/basic\"\n\t\"go.opentelemetry.io/otel/sdk/metric/selector/simple\"\n)\n\n// Controller represents the controller to send metrics to.\ntype Controller interface {\n\tisController()\n}\n\n// OTELGRPC represents exporting to the \"go.opentelemetry.io/otel/sdk/metric/controller/basic\" controller.\ntype OTELGRPC struct {\n\t// Addr is the local address to export on.\n\tAddr string\n}\n\nfunc (o OTELGRPC) isController() {}\n\n// Stop is used to stop OTEL metric handling.\ntype Stop func()\n\n// Start is used to start OTEL metric handling.\nfunc Start(ctx context.Context, c Controller) (Stop, error) {\n\tcontrol, err := newController(ctx, c)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\terr = control.Start(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn func() {\n\t\tctx, cancel := context.WithTimeout(ctx, 1*time.Second)\n\t\tdefer cancel()\n\t\tif err := control.Stop(ctx); err != nil {\n\t\t\totel.Handle(err)\n\t\t}\n\t}, nil\n}\n\nfunc newController(ctx context.Context, c Controller) (*basic.Controller, error) {\n\tswitch v := c.(type) {\n\tcase OTELGRPC:\n\t\treturn otelGRPC(ctx, v)\n\t}\n\treturn nil, fmt.Errorf(\"%T is not a valid Controller\", c)\n}\n\nfunc otelGRPC(ctx context.Context, args OTELGRPC) (*basic.Controller, error) {\n\tmetricClient := otlpmetricgrpc.NewClient(\n\t\totlpmetricgrpc.WithInsecure(),\n\t\totlpmetricgrpc.WithEndpoint(args.Addr),\n\t)\n\tmetricExp, err := otlpmetric.New(ctx, metricClient)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"Failed to create the collector metric exporter\")\n\t}\n\n\tpusher := basic.New(\n\t\tprocessor.NewFactory(\n\t\t\tsimple.NewWithHistogramDistribution(),\n\t\t\tmetricExp,\n\t\t),\n\t\tbasic.WithExporter(metricExp),\n\t\tbasic.WithCollectPeriod(10*time.Second),\n\t)\n\tglobal.SetMeterProvider(pusher)\n\treturn pusher, nil\n}\n"
  },
  {
    "path": "chapter/11/petstore/internal/server/telemetry/tracing/sampler/sampler.go",
    "content": "/*\nPackage sampler offers a Sampler that looks for a TraceID.Valid() == true or a gRPC metadata key called \"trace\"\nand if they exist will sample. Otherwise it looks to a child Sampler to determine based upon whatever sampler\nalgorithm is used.\n\nIn addition we offer the ability to switch out the underlying sampler at anytime in a thread-safe way.\n\nYou can construct a new Sampler like so:\n\ts, err := New(trace.NeverSample)\n\tif err != nil {\n\t\t// Do something\n\t}\n\nThe above Sampler would only trace if a TraceID.Valid() == true or gRCP metadate key called \"trace\" existed.\n\nIf we want to trace 1% of the time as well, we can do the following:\n\ts, err := New(trace.TraceIDRatioBased(.01))\n\tif err != nil {\n\t\t// Do something\n\t}\n*/\npackage sampler\n\nimport (\n\t\"fmt\"\n\t\"sync/atomic\"\n\n\t\"go.opentelemetry.io/otel/sdk/trace\"\n\totelTrace \"go.opentelemetry.io/otel/trace\"\n\t\"google.golang.org/grpc/metadata\"\n)\n\nconst desc = `This sampler samples if TracdID.Valid(), gRPC metadata contains key \"trace\" or the child sampler decides to sample`\n\n// Sampler decides whether a trace should be sampled and exported. This sampler will sample if\n// paramters TraceID.Valid() == true or the Context contains gRPC metadata that has key \"trace\" (it doesn't care about values).\ntype Sampler struct {\n\t// child stores a *trace.Sampler. trace.Sampler is an interface. Because atomic.Value cares about\n\t// the underlying type, you can't just store trace.Sampler. So we do a pointer, which is the only valid\n\t// use of *interface I've ever seen.\n\tchild atomic.Value // *trace.Sampler\n}\n\n// New creates a new Sampler with the child Sampler used if TraceID.Valid() == false and gRPC metadata does not contain\n// key \"trace\".\nfunc New(child trace.Sampler) (*Sampler, error) {\n\tif child == nil {\n\t\treturn nil, fmt.Errorf(\"child cannot == nil\")\n\t}\n\n\ts := &Sampler{}\n\ts.child.Store(&child)\n\treturn s, nil\n}\n\n// ShouldSample implements trace.Sampler.ShouldSample.\nfunc (s *Sampler) ShouldSample(p trace.SamplingParameters) trace.SamplingResult {\n\tpsc := otelTrace.SpanContextFromContext(p.ParentContext)\n\tif psc.IsValid() {\n\t\tif psc.IsRemote() {\n\t\t\tif psc.IsSampled() {\n\t\t\t\treturn trace.SamplingResult{\n\t\t\t\t\tDecision:   trace.RecordAndSample,\n\t\t\t\t\tTracestate: psc.TraceState(),\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif psc.IsSampled() {\n\t\t\treturn trace.SamplingResult{\n\t\t\t\tDecision:   trace.RecordAndSample,\n\t\t\t\tTracestate: psc.TraceState(),\n\t\t\t}\n\t\t}\n\t}\n\tmd, ok := metadata.FromIncomingContext(p.ParentContext)\n\tif !ok {\n\t\treturn (*s.child.Load().(*trace.Sampler)).ShouldSample(p)\n\t}\n\n\tif _, ok := md[\"trace\"]; ok {\n\t\tpsc := otelTrace.SpanContextFromContext(p.ParentContext)\n\t\treturn trace.SamplingResult{\n\t\t\tDecision:   trace.RecordAndSample,\n\t\t\tTracestate: psc.TraceState(),\n\t\t}\n\t}\n\n\treturn (*s.child.Load().(*trace.Sampler)).ShouldSample(p)\n}\n\n// Description implements trace.Sampler.Description().\nfunc (s *Sampler) Description() string {\n\treturn desc\n}\n\n// Switch switches the underlying trace.Sampler.\nfunc (s *Sampler) Switch(sampler trace.Sampler) {\n\tif sampler == nil {\n\t\tpanic(\"cannot call Switch() with a nil Sampler\")\n\t}\n\ts.child.Store(&sampler)\n}\n"
  },
  {
    "path": "chapter/11/petstore/internal/server/telemetry/tracing/tracing.go",
    "content": "/*\nPackage tracing provides functions for starting and stopping our Open Telemetry tracing.\nThis package is intended to be used from main and is simple to use. We offer a few\nchoices on where traces export to. Here is an example to trace to stderr for all requests:\n\tfunc main() {\n\t\tctx := context.Background()\n\t\t// Set us up to always sample. The \"trace\" package is: \"petstore/server/SearchPets/latency\"\n\t\ttracing.Sampler.Switch(trace.AlwaysSample())\n\t\t// Start our tracing and pass the empty Stderr tracing arguments.\n\t\t// Stderr{} has no required fields.\n\t\tstop, err := tracing.Start(ctx, tracing.Stderr{})\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"problem starting telemetry: %s\", err)\n\t\t}\n\n\t\t// Stop kills our exporter when main() ends.\n\t\tdefer stop()\n\t}\n*/\npackage tracing\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/internal/server/telemetry/tracing/sampler\"\n\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc\"\n\t\"go.opentelemetry.io/otel/exporters/stdout/stdouttrace\"\n\t\"go.opentelemetry.io/otel/propagation\"\n\t\"go.opentelemetry.io/otel/sdk/resource\"\n\tsdktrace \"go.opentelemetry.io/otel/sdk/trace\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.4.0\"\n\t\"go.opentelemetry.io/otel/trace\"\n\t\"google.golang.org/grpc\"\n)\n\n// Tracer is the tracer initialized by Start().\nvar (\n\t// Tracer is the tracer initialized by Start().\n\tTracer trace.Tracer // *sdktrace.TracerProvider //otlptrace.Exporter\n\t// Sampler is our *sampler.Sampler used by the Tracer.\n\tSampler *sampler.Sampler\n)\n\nfunc init() {\n\ts, err := sampler.New(sdktrace.TraceIDRatioBased(1))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tSampler = s\n}\n\n// Exporter represents the exporter to send telemetry to.\ntype Exporter interface {\n\tisExporter()\n}\n\n// OTELGRPC represents exporting to the go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc exporter.\ntype OTELGRPC struct {\n\t// Addr is the local address to export on.\n\tAddr string\n}\n\nfunc (o OTELGRPC) isExporter() {}\n\n// Stderr exports trace data to os.Stderr.\ntype Stderr struct{}\n\nfunc (s Stderr) isExporter() {}\n\n// File exports trace data to a file. If the file exists, it is overwritten.\ntype File struct {\n\t// Path is the path to the file.\n\tPath string\n}\n\nfunc (f File) isExporter() {}\n\n// Stop stops our Open Telemetry exporter.\ntype Stop func()\n\n// Start creates the OTEL exporter and configures the trace providers.\n// It returns a Stop() which will stop the exporter.\nfunc Start(ctx context.Context, e Exporter) (Stop, error) {\n\tlog.Println(\"Sampler: \", Sampler)\n\ttp, err := newTraceExporter(ctx, e)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tTracer = tp.Tracer(\"petstore\")\n\n\treturn func() {\n\t\tvar cancel context.CancelFunc\n\t\tctx, cancel = context.WithTimeout(ctx, 1*time.Second)\n\t\tdefer cancel()\n\n\t\tif err := tp.Shutdown(ctx); err != nil {\n\t\t\totel.Handle(err)\n\t\t}\n\t}, nil\n}\n\n// newTracerExporter creates an OTLP exporter with our tracer information.\nfunc newTraceExporter(ctx context.Context, e Exporter) (*sdktrace.TracerProvider, error) {\n\tvar exp sdktrace.SpanExporter\n\tvar err error\n\tswitch v := e.(type) {\n\tcase OTELGRPC:\n\t\texp, err = otelGRPC(ctx, v)\n\tcase Stderr:\n\t\texp, err = newFileExporter(os.Stderr)\n\tcase File:\n\t\tf, err := os.Create(v.Path)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\texp, err = newFileExporter(f)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"%T is not a valid Exporter\", e)\n\t}\n\n\tres, err := resource.New(\n\t\tctx,\n\t\tresource.WithFromEnv(),\n\t\tresource.WithProcess(),\n\t\tresource.WithTelemetrySDK(),\n\t\tresource.WithHost(),\n\t\tresource.WithAttributes(\n\t\t\t// the service name used to display traces in backends\n\t\t\tsemconv.ServiceNameKey.String(\"petstore\"),\n\t\t),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// set global propagator to tracecontext (the default is no-op).\n\totel.SetTextMapPropagator(propagation.TraceContext{})\n\n\tprov := sdktrace.NewTracerProvider(\n\t\tsdktrace.WithSampler(Sampler),\n\t\tsdktrace.WithBatcher(exp),\n\t\tsdktrace.WithResource(res),\n\t)\n\totel.SetTracerProvider(prov)\n\treturn prov, nil\n}\n\n// newFileExporter creates an exporter that writes to a file.\nfunc newFileExporter(w io.Writer) (sdktrace.SpanExporter, error) {\n\treturn stdouttrace.New(\n\t\tstdouttrace.WithWriter(w),\n\t\tstdouttrace.WithPrettyPrint(),\n\t)\n}\n\nfunc otelGRPC(ctx context.Context, e OTELGRPC) (sdktrace.SpanExporter, error) { //(*otlptrace.Exporter, error) {\n\texp, err := otlptrace.New(\n\t\tctx,\n\t\totlptracegrpc.NewClient(\n\t\t\totlptracegrpc.WithInsecure(),\n\t\t\totlptracegrpc.WithEndpoint(e.Addr),\n\t\t\totlptracegrpc.WithDialOption(grpc.WithBlock()),\n\t\t),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn exp, nil\n}\n"
  },
  {
    "path": "chapter/11/petstore/otel-collector-config.yaml",
    "content": "receivers:\n  otlp:\n    protocols:\n      grpc:\n\nexporters:\n  prometheus:\n    endpoint: \"0.0.0.0:8889\"\n    const_labels:\n      label1: value1\n  logging:\n\n  jaeger:\n    endpoint: jaeger-all-in-one:14250\n    tls:\n      insecure: true\n\nprocessors:\n  batch:\n\nservice:\n  pipelines:\n    traces:\n      receivers: [otlp]\n      processors: [batch]\n      exporters: [jaeger]\n    metrics:\n      receivers: [otlp]\n      processors: [batch]\n      exporters: [prometheus]\n"
  },
  {
    "path": "chapter/11/petstore/petstore.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\tstdlog \"log\"\n\t\"os\"\n\t\"strconv\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/internal/server\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/internal/server/log\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/internal/server/storage/mem\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/internal/server/telemetry/metrics\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore/internal/server/telemetry/tracing\"\n\n\t//grpcotel \"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc\"\n\t\"go.opentelemetry.io/otel/sdk/trace\"\n)\n\n// General service flags.\nvar (\n\taddr = flag.String(\"addr\", \"0.0.0.0:6742\", \"The address to run the service on.\")\n)\n\n// Flags are related to OTEL tracing.\nvar (\n\tlocalDebug = flag.Bool(\"localDebug\", false, \"If true, OTEL traces are sent to the console\")\n\tfileDebug  = flag.String(\"fileDebug\", \"\", \"If set, OTEL traces are written to the file path provided\")\n\tgrpcTraces = flag.Bool(\"grpcTraces\", false, \"Our traces are exported via gRPC. Must set otelAddr.\")\n\n\ttraceSampling = flag.String(\"traceSampling\", \"never\", \"Sets the sampling type. By default we never sample unless it is requested by the client.\"+\n\t\t\"Valid values are: 'never', 'always' and '[float]', where float is a floating point value where any value over 1 is all.\",\n\t)\n)\n\n// These flags relate to exporting our Open Telemetry traces via gRPC.\nvar (\n\totelAddr = flag.String(\"otelAddr\", \"\", \"The address for our OpenTelemetry agent. If not set, looks for Env variable 'OTEL_EXPORTER_OTLP_ENDPOINT'. If not set defaults to 0.0.0:4317\")\n)\n\nfunc init() {\n\tif *otelAddr == \"\" {\n\t\taddr, ok := os.LookupEnv(\"OTEL_EXPORTER_OTLP_ENDPOINT\")\n\t\tif !ok {\n\t\t\taddr = \"0.0.0.0:4317\"\n\t\t}\n\t\t*otelAddr = addr\n\t}\n}\n\n// otelExporter determines if the flags are set to export tracing information\n// to a destination. If so, we return the arguments needed for that exporter.\nfunc otelExporter() tracing.Exporter {\n\tif tooManyTrue(*localDebug, *fileDebug, *grpcTraces) {\n\t\tlog.Logger.Fatalf(\"cannot set more than one from this list: localDebug, fileDebug, grpcTraces\")\n\t}\n\n\tswitch {\n\tcase *localDebug:\n\t\treturn tracing.Stderr{}\n\tcase *fileDebug != \"\":\n\t\treturn tracing.File{Path: *fileDebug}\n\tcase *grpcTraces:\n\t\treturn tracing.OTELGRPC{Addr: *otelAddr}\n\t}\n\treturn tracing.Stderr{}\n}\n\n// otelController is similar to otelExporter except it sets up arguments for metric\n// exporting.\nfunc otelController() metrics.Controller {\n\tif *otelAddr != \"\" {\n\t\treturn metrics.OTELGRPC{Addr: *otelAddr}\n\t}\n\treturn nil\n}\n\n// setSampling checks flags and then sets our tracing sampling rate.\nfunc setSampling() {\n\tswitch *traceSampling {\n\tcase \"never\":\n\t\ttracing.Sampler.Switch(trace.NeverSample())\n\t\treturn\n\tcase \"always\":\n\t\ttracing.Sampler.Switch(trace.AlwaysSample())\n\t\treturn\n\tdefault:\n\t\tif f, err := strconv.ParseFloat(*traceSampling, 64); err == nil {\n\t\t\ttracing.Sampler.Switch(trace.TraceIDRatioBased(f))\n\t\t\treturn\n\t\t}\n\t}\n\tlog.Logger.Fatalf(\"traceSampling=%s is not a valid value\", *traceSampling)\n}\n\n// tooManyTrue is given a list of bool or string types. A string type that\n// is non-empty string is considered true. If more than one value is true,\n// this returns true. Otherwise it returns false.\nfunc tooManyTrue(truths ...interface{}) bool {\n\tset := false\n\tfor _, t := range truths {\n\t\tswitch v := t.(type) {\n\t\tcase bool:\n\t\t\tif v && set {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif v {\n\t\t\t\tset = true\n\t\t\t}\n\t\tcase string:\n\t\t\tif v != \"\" && set {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif v != \"\" {\n\t\t\t\tset = true\n\t\t\t}\n\t\tdefault:\n\t\t\tpanic(\"not a bool or string\")\n\t\t}\n\t}\n\treturn false\n}\n\nfunc main() {\n\tflag.Parse()\n\tlog.Logger.Println(\"Flags values\")\n\tlog.Logger.Println(\"-----------------------------------\")\n\tflag.VisitAll(func(f *flag.Flag) {\n\t\tlog.Logger.Printf(\"%s: %s\\n\", f.Name, f.Value)\n\t})\n\tlog.Logger.Println(\"-----------------------------------\")\n\n\tstdlog.SetFlags(stdlog.LstdFlags | stdlog.Lshortfile)\n\tlog.SetFlags(log.LstdFlags | log.Lshortfile)\n\tlog.Logger.SetFlags(stdlog.LstdFlags | stdlog.Lshortfile)\n\n\tctx := context.Background()\n\n\t// Setup for OTEL tracing.\n\tsetSampling()\n\te := otelExporter()\n\tif e != nil {\n\t\tstop, err := tracing.Start(ctx, e)\n\t\tif err != nil {\n\t\t\tlog.Logger.Fatalf(\"problem starting telemetry: %s\", err)\n\t\t}\n\t\tdefer stop()\n\t}\n\n\t// Setup for OTEL metrics.\n\tc := otelController()\n\tif c != nil {\n\t\tstop, err := metrics.Start(ctx, c)\n\t\tif err != nil {\n\t\t\tlog.Logger.Fatal(err)\n\t\t}\n\t\tdefer stop()\n\t}\n\n\t// Setup for the service.\n\tstore := mem.New()\n\n\ts, err := server.New(\n\t\t*addr,\n\t\tstore,\n\t\tserver.WithGRPCOpts(\n\t\t//grpc.UnaryInterceptor(grpcotel.UnaryServerInterceptor(tracing.Tracer)),\n\t\t//grpc.StreamInterceptor(grpcotel.StreamServerInterceptor(tracing.Tracer)),\n\t\t),\n\t)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tdone := make(chan error, 1)\n\n\tlog.Logger.Println(\"Starting server at: \", *addr)\n\tgo func() {\n\t\tdefer close(done)\n\t\tdone <- s.Start()\n\t}()\n\n\tlog.Logger.Println(\"Server exited with error: \", <-done)\n}\n"
  },
  {
    "path": "chapter/11/petstore/prometheus.yaml",
    "content": "scrape_configs:\n  - job_name: 'otel-collector'\n    scrape_interval: 10s\n    static_configs:\n      - targets: ['otel-collector:8889']\n      - targets: ['otel-collector:8888']\n"
  },
  {
    "path": "chapter/11/petstore/proto/buf.gen.yaml",
    "content": "version: v1\nplugins:\n  - name: go\n    out: ./\n    opt:\n      - paths=source_relative\n  - name: go-grpc\n    out: ./\n    opt:\n      - paths=source_relative\n"
  },
  {
    "path": "chapter/11/petstore/proto/buf.yaml",
    "content": "version: v1\ndeps:\n  - buf.build/googleapis/googleapis\nlint:\n  use:\n    - DEFAULT\nbreaking:\n  use:\n    - FILE\n"
  },
  {
    "path": "chapter/11/petstore/proto/petstore.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.25.0-devel\n// \tprotoc        v3.18.0\n// source: petstore.proto\n\npackage proto\n\nimport (\n\tdate \"google.golang.org/genproto/googleapis/type/date\"\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\n// Desribes the type of pets.\ntype PetType int32\n\nconst (\n\t// The type was not set.\n\tPetType_PTUnknown PetType = 0\n\t// The pet is a canine.\n\tPetType_PTCanine PetType = 1\n\t// The pet is a feline.\n\tPetType_PTFeline PetType = 2\n\t// The pet is a bird.\n\tPetType_PTBird PetType = 3\n\t// The pet is a reptile.\n\tPetType_PTReptile PetType = 4\n)\n\n// Enum value maps for PetType.\nvar (\n\tPetType_name = map[int32]string{\n\t\t0: \"PTUnknown\",\n\t\t1: \"PTCanine\",\n\t\t2: \"PTFeline\",\n\t\t3: \"PTBird\",\n\t\t4: \"PTReptile\",\n\t}\n\tPetType_value = map[string]int32{\n\t\t\"PTUnknown\": 0,\n\t\t\"PTCanine\":  1,\n\t\t\"PTFeline\":  2,\n\t\t\"PTBird\":    3,\n\t\t\"PTReptile\": 4,\n\t}\n)\n\nfunc (x PetType) Enum() *PetType {\n\tp := new(PetType)\n\t*p = x\n\treturn p\n}\n\nfunc (x PetType) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (PetType) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_petstore_proto_enumTypes[0].Descriptor()\n}\n\nfunc (PetType) Type() protoreflect.EnumType {\n\treturn &file_petstore_proto_enumTypes[0]\n}\n\nfunc (x PetType) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use PetType.Descriptor instead.\nfunc (PetType) EnumDescriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{0}\n}\n\n// Types of OTEL sampling we support.\ntype SamplerType int32\n\nconst (\n\tSamplerType_STUnknown SamplerType = 0\n\tSamplerType_STNever   SamplerType = 1\n\tSamplerType_STAlways  SamplerType = 2\n\tSamplerType_STFloat   SamplerType = 3\n)\n\n// Enum value maps for SamplerType.\nvar (\n\tSamplerType_name = map[int32]string{\n\t\t0: \"STUnknown\",\n\t\t1: \"STNever\",\n\t\t2: \"STAlways\",\n\t\t3: \"STFloat\",\n\t}\n\tSamplerType_value = map[string]int32{\n\t\t\"STUnknown\": 0,\n\t\t\"STNever\":   1,\n\t\t\"STAlways\":  2,\n\t\t\"STFloat\":   3,\n\t}\n)\n\nfunc (x SamplerType) Enum() *SamplerType {\n\tp := new(SamplerType)\n\t*p = x\n\treturn p\n}\n\nfunc (x SamplerType) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (SamplerType) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_petstore_proto_enumTypes[1].Descriptor()\n}\n\nfunc (SamplerType) Type() protoreflect.EnumType {\n\treturn &file_petstore_proto_enumTypes[1]\n}\n\nfunc (x SamplerType) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use SamplerType.Descriptor instead.\nfunc (SamplerType) EnumDescriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{1}\n}\n\n// Represents a range of dates.\ntype DateRange struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// When to start the range, this is inclusive.\n\tStart *date.Date `protobuf:\"bytes,1,opt,name=start,proto3\" json:\"start,omitempty\"`\n\t// When to end the range, this is exclusive.\n\tEnd *date.Date `protobuf:\"bytes,2,opt,name=end,proto3\" json:\"end,omitempty\"`\n}\n\nfunc (x *DateRange) Reset() {\n\t*x = DateRange{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DateRange) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DateRange) ProtoMessage() {}\n\nfunc (x *DateRange) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_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 DateRange.ProtoReflect.Descriptor instead.\nfunc (*DateRange) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *DateRange) GetStart() *date.Date {\n\tif x != nil {\n\t\treturn x.Start\n\t}\n\treturn nil\n}\n\nfunc (x *DateRange) GetEnd() *date.Date {\n\tif x != nil {\n\t\treturn x.End\n\t}\n\treturn nil\n}\n\n// Represents a unique pet.\ntype Pet struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// A UUIDv4 for this pet. This can never be set on an AddPet().\n\tId string `protobuf:\"bytes,1,opt,name=id,proto3\" json:\"id,omitempty\"`\n\t// The name of the pet.\n\tName string `protobuf:\"bytes,2,opt,name=name,proto3\" json:\"name,omitempty\"`\n\t// The type of pet.\n\tType PetType `protobuf:\"varint,3,opt,name=type,proto3,enum=petstore.PetType\" json:\"type,omitempty\"`\n\t// The pet's birthday.\n\tBirthday *date.Date `protobuf:\"bytes,4,opt,name=birthday,proto3\" json:\"birthday,omitempty\"`\n}\n\nfunc (x *Pet) Reset() {\n\t*x = Pet{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Pet) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Pet) ProtoMessage() {}\n\nfunc (x *Pet) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_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 Pet.ProtoReflect.Descriptor instead.\nfunc (*Pet) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *Pet) GetId() string {\n\tif x != nil {\n\t\treturn x.Id\n\t}\n\treturn \"\"\n}\n\nfunc (x *Pet) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\nfunc (x *Pet) GetType() PetType {\n\tif x != nil {\n\t\treturn x.Type\n\t}\n\treturn PetType_PTUnknown\n}\n\nfunc (x *Pet) GetBirthday() *date.Date {\n\tif x != nil {\n\t\treturn x.Birthday\n\t}\n\treturn nil\n}\n\n// The request used to add a pets to the system.\ntype AddPetsReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The pet information to add. Pet.id must not be set.\n\tPets []*Pet `protobuf:\"bytes,1,rep,name=pets,proto3\" json:\"pets,omitempty\"`\n}\n\nfunc (x *AddPetsReq) Reset() {\n\t*x = AddPetsReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AddPetsReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AddPetsReq) ProtoMessage() {}\n\nfunc (x *AddPetsReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[2]\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 AddPetsReq.ProtoReflect.Descriptor instead.\nfunc (*AddPetsReq) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *AddPetsReq) GetPets() []*Pet {\n\tif x != nil {\n\t\treturn x.Pets\n\t}\n\treturn nil\n}\n\n// The response do AddPets().\ntype AddPetsResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The IDs of the pets that were added.\n\tIds []string `protobuf:\"bytes,1,rep,name=ids,proto3\" json:\"ids,omitempty\"`\n}\n\nfunc (x *AddPetsResp) Reset() {\n\t*x = AddPetsResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AddPetsResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AddPetsResp) ProtoMessage() {}\n\nfunc (x *AddPetsResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[3]\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 AddPetsResp.ProtoReflect.Descriptor instead.\nfunc (*AddPetsResp) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{3}\n}\n\nfunc (x *AddPetsResp) GetIds() []string {\n\tif x != nil {\n\t\treturn x.Ids\n\t}\n\treturn nil\n}\n\n// The request used to update pets in the system.\ntype UpdatePetsReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The pet information to update. Pet.id must be set.\n\tPets []*Pet `protobuf:\"bytes,1,rep,name=pets,proto3\" json:\"pets,omitempty\"`\n}\n\nfunc (x *UpdatePetsReq) Reset() {\n\t*x = UpdatePetsReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[4]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *UpdatePetsReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*UpdatePetsReq) ProtoMessage() {}\n\nfunc (x *UpdatePetsReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[4]\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 UpdatePetsReq.ProtoReflect.Descriptor instead.\nfunc (*UpdatePetsReq) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (x *UpdatePetsReq) GetPets() []*Pet {\n\tif x != nil {\n\t\treturn x.Pets\n\t}\n\treturn nil\n}\n\n// The response do UpdatePets().\ntype UpdatePetsResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *UpdatePetsResp) Reset() {\n\t*x = UpdatePetsResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[5]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *UpdatePetsResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*UpdatePetsResp) ProtoMessage() {}\n\nfunc (x *UpdatePetsResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[5]\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 UpdatePetsResp.ProtoReflect.Descriptor instead.\nfunc (*UpdatePetsResp) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{5}\n}\n\n// Used to indicate which pets to delete. This is an all or nothing request.\ntype DeletePetsReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The IDs of the pets to delete.\n\tIds []string `protobuf:\"bytes,1,rep,name=ids,proto3\" json:\"ids,omitempty\"`\n}\n\nfunc (x *DeletePetsReq) Reset() {\n\t*x = DeletePetsReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[6]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DeletePetsReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DeletePetsReq) ProtoMessage() {}\n\nfunc (x *DeletePetsReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[6]\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 DeletePetsReq.ProtoReflect.Descriptor instead.\nfunc (*DeletePetsReq) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{6}\n}\n\nfunc (x *DeletePetsReq) GetIds() []string {\n\tif x != nil {\n\t\treturn x.Ids\n\t}\n\treturn nil\n}\n\n// The response to a DeletePet().\ntype DeletePetsResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *DeletePetsResp) Reset() {\n\t*x = DeletePetsResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[7]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DeletePetsResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DeletePetsResp) ProtoMessage() {}\n\nfunc (x *DeletePetsResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[7]\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 DeletePetsResp.ProtoReflect.Descriptor instead.\nfunc (*DeletePetsResp) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{7}\n}\n\n// The request to search for pets.\ntype SearchPetsReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Pet names to filter by.\n\tNames []string `protobuf:\"bytes,1,rep,name=names,proto3\" json:\"names,omitempty\"`\n\t// Pet types to filter by.\n\tTypes []PetType `protobuf:\"varint,2,rep,packed,name=types,proto3,enum=petstore.PetType\" json:\"types,omitempty\"`\n\t// Birthdays to filter by.\n\tBirthdateRange *DateRange `protobuf:\"bytes,3,opt,name=birthdate_range,json=birthdateRange,proto3\" json:\"birthdate_range,omitempty\"`\n}\n\nfunc (x *SearchPetsReq) Reset() {\n\t*x = SearchPetsReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[8]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *SearchPetsReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*SearchPetsReq) ProtoMessage() {}\n\nfunc (x *SearchPetsReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[8]\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 SearchPetsReq.ProtoReflect.Descriptor instead.\nfunc (*SearchPetsReq) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{8}\n}\n\nfunc (x *SearchPetsReq) GetNames() []string {\n\tif x != nil {\n\t\treturn x.Names\n\t}\n\treturn nil\n}\n\nfunc (x *SearchPetsReq) GetTypes() []PetType {\n\tif x != nil {\n\t\treturn x.Types\n\t}\n\treturn nil\n}\n\nfunc (x *SearchPetsReq) GetBirthdateRange() *DateRange {\n\tif x != nil {\n\t\treturn x.BirthdateRange\n\t}\n\treturn nil\n}\n\ntype Sampler struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The type of sampling to change to.\n\tType SamplerType `protobuf:\"varint,1,opt,name=type,proto3,enum=petstore.SamplerType\" json:\"type,omitempty\"`\n\t// This is the sampling rate if type == STFloat. Values must be\n\t// > 0 and <= 1.0 .\n\tFloatValue float64 `protobuf:\"fixed64,2,opt,name=float_value,json=floatValue,proto3\" json:\"float_value,omitempty\"`\n}\n\nfunc (x *Sampler) Reset() {\n\t*x = Sampler{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[9]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Sampler) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Sampler) ProtoMessage() {}\n\nfunc (x *Sampler) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[9]\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 Sampler.ProtoReflect.Descriptor instead.\nfunc (*Sampler) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{9}\n}\n\nfunc (x *Sampler) GetType() SamplerType {\n\tif x != nil {\n\t\treturn x.Type\n\t}\n\treturn SamplerType_STUnknown\n}\n\nfunc (x *Sampler) GetFloatValue() float64 {\n\tif x != nil {\n\t\treturn x.FloatValue\n\t}\n\treturn 0\n}\n\n// Used to request we change the OTEL sampling.\ntype ChangeSamplerReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tSampler *Sampler `protobuf:\"bytes,1,opt,name=sampler,proto3\" json:\"sampler,omitempty\"`\n}\n\nfunc (x *ChangeSamplerReq) Reset() {\n\t*x = ChangeSamplerReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[10]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ChangeSamplerReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ChangeSamplerReq) ProtoMessage() {}\n\nfunc (x *ChangeSamplerReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[10]\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 ChangeSamplerReq.ProtoReflect.Descriptor instead.\nfunc (*ChangeSamplerReq) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{10}\n}\n\nfunc (x *ChangeSamplerReq) GetSampler() *Sampler {\n\tif x != nil {\n\t\treturn x.Sampler\n\t}\n\treturn nil\n}\n\n// The response to a sampling change.\ntype ChangeSamplerResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ChangeSamplerResp) Reset() {\n\t*x = ChangeSamplerResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[11]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ChangeSamplerResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ChangeSamplerResp) ProtoMessage() {}\n\nfunc (x *ChangeSamplerResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[11]\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 ChangeSamplerResp.ProtoReflect.Descriptor instead.\nfunc (*ChangeSamplerResp) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{11}\n}\n\nvar File_petstore_proto protoreflect.FileDescriptor\n\nvar file_petstore_proto_rawDesc = []byte{\n\t0x0a, 0x0e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x12, 0x08, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x1a, 0x16, 0x67, 0x6f, 0x6f, 0x67,\n\t0x6c, 0x65, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x22, 0x59, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12,\n\t0x27, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11,\n\t0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x44, 0x61, 0x74,\n\t0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x23, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74,\n\t0x79, 0x70, 0x65, 0x2e, 0x44, 0x61, 0x74, 0x65, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x7f, 0x0a,\n\t0x03, 0x50, 0x65, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,\n\t0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,\n\t0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,\n\t0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72,\n\t0x65, 0x2e, 0x50, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12,\n\t0x2d, 0x0a, 0x08, 0x62, 0x69, 0x72, 0x74, 0x68, 0x64, 0x61, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28,\n\t0x0b, 0x32, 0x11, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e,\n\t0x44, 0x61, 0x74, 0x65, 0x52, 0x08, 0x62, 0x69, 0x72, 0x74, 0x68, 0x64, 0x61, 0x79, 0x22, 0x2f,\n\t0x0a, 0x0a, 0x41, 0x64, 0x64, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x12, 0x21, 0x0a, 0x04,\n\t0x70, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x65, 0x74,\n\t0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x65, 0x74, 0x52, 0x04, 0x70, 0x65, 0x74, 0x73, 0x22,\n\t0x1f, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10,\n\t0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x73,\n\t0x22, 0x32, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65,\n\t0x71, 0x12, 0x21, 0x0a, 0x04, 0x70, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,\n\t0x0d, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x65, 0x74, 0x52, 0x04,\n\t0x70, 0x65, 0x74, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65,\n\t0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x21, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,\n\t0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01,\n\t0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x44, 0x65, 0x6c,\n\t0x65, 0x74, 0x65, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x8c, 0x01, 0x0a, 0x0d,\n\t0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a,\n\t0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61,\n\t0x6d, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03,\n\t0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x65,\n\t0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0f,\n\t0x62, 0x69, 0x72, 0x74, 0x68, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18,\n\t0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65,\n\t0x2e, 0x44, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0e, 0x62, 0x69, 0x72, 0x74,\n\t0x68, 0x64, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x55, 0x0a, 0x07, 0x53, 0x61,\n\t0x6d, 0x70, 0x6c, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x53,\n\t0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,\n\t0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x56, 0x61, 0x6c, 0x75,\n\t0x65, 0x22, 0x3f, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x61, 0x6d, 0x70, 0x6c,\n\t0x65, 0x72, 0x52, 0x65, 0x71, 0x12, 0x2b, 0x0a, 0x07, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72,\n\t0x65, 0x2e, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x52, 0x07, 0x73, 0x61, 0x6d, 0x70, 0x6c,\n\t0x65, 0x72, 0x22, 0x13, 0x0a, 0x11, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x61, 0x6d, 0x70,\n\t0x6c, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x2a, 0x4f, 0x0a, 0x07, 0x50, 0x65, 0x74, 0x54, 0x79,\n\t0x70, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x54, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10,\n\t0x00, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x54, 0x43, 0x61, 0x6e, 0x69, 0x6e, 0x65, 0x10, 0x01, 0x12,\n\t0x0c, 0x0a, 0x08, 0x50, 0x54, 0x46, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x10, 0x02, 0x12, 0x0a, 0x0a,\n\t0x06, 0x50, 0x54, 0x42, 0x69, 0x72, 0x64, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x54, 0x52,\n\t0x65, 0x70, 0x74, 0x69, 0x6c, 0x65, 0x10, 0x04, 0x2a, 0x44, 0x0a, 0x0b, 0x53, 0x61, 0x6d, 0x70,\n\t0x6c, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x54, 0x55, 0x6e, 0x6b,\n\t0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x4e, 0x65, 0x76, 0x65,\n\t0x72, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x54, 0x41, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x10,\n\t0x02, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x10, 0x03, 0x32, 0xd0,\n\t0x02, 0x0a, 0x08, 0x50, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x41,\n\t0x64, 0x64, 0x50, 0x65, 0x74, 0x73, 0x12, 0x14, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72,\n\t0x65, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x70,\n\t0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x65, 0x74, 0x73, 0x52,\n\t0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50,\n\t0x65, 0x74, 0x73, 0x12, 0x17, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x55,\n\t0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x70,\n\t0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65,\n\t0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65,\n\t0x74, 0x65, 0x50, 0x65, 0x74, 0x73, 0x12, 0x17, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72,\n\t0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x1a,\n\t0x18, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74,\n\t0x65, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x38, 0x0a, 0x0a, 0x53,\n\t0x65, 0x61, 0x72, 0x63, 0x68, 0x50, 0x65, 0x74, 0x73, 0x12, 0x17, 0x2e, 0x70, 0x65, 0x74, 0x73,\n\t0x74, 0x6f, 0x72, 0x65, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x50, 0x65, 0x74, 0x73, 0x52,\n\t0x65, 0x71, 0x1a, 0x0d, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x65,\n\t0x74, 0x22, 0x00, 0x30, 0x01, 0x12, 0x4a, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53,\n\t0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72,\n\t0x65, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x52,\n\t0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x68,\n\t0x61, 0x6e, 0x67, 0x65, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x22,\n\t0x00, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,\n\t0x50, 0x61, 0x63, 0x6b, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x2f,\n\t0x47, 0x6f, 0x2d, 0x66, 0x6f, 0x72, 0x2d, 0x44, 0x65, 0x76, 0x4f, 0x70, 0x73, 0x2f, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_petstore_proto_rawDescOnce sync.Once\n\tfile_petstore_proto_rawDescData = file_petstore_proto_rawDesc\n)\n\nfunc file_petstore_proto_rawDescGZIP() []byte {\n\tfile_petstore_proto_rawDescOnce.Do(func() {\n\t\tfile_petstore_proto_rawDescData = protoimpl.X.CompressGZIP(file_petstore_proto_rawDescData)\n\t})\n\treturn file_petstore_proto_rawDescData\n}\n\nvar file_petstore_proto_enumTypes = make([]protoimpl.EnumInfo, 2)\nvar file_petstore_proto_msgTypes = make([]protoimpl.MessageInfo, 12)\nvar file_petstore_proto_goTypes = []interface{}{\n\t(PetType)(0),              // 0: petstore.PetType\n\t(SamplerType)(0),          // 1: petstore.SamplerType\n\t(*DateRange)(nil),         // 2: petstore.DateRange\n\t(*Pet)(nil),               // 3: petstore.Pet\n\t(*AddPetsReq)(nil),        // 4: petstore.AddPetsReq\n\t(*AddPetsResp)(nil),       // 5: petstore.AddPetsResp\n\t(*UpdatePetsReq)(nil),     // 6: petstore.UpdatePetsReq\n\t(*UpdatePetsResp)(nil),    // 7: petstore.UpdatePetsResp\n\t(*DeletePetsReq)(nil),     // 8: petstore.DeletePetsReq\n\t(*DeletePetsResp)(nil),    // 9: petstore.DeletePetsResp\n\t(*SearchPetsReq)(nil),     // 10: petstore.SearchPetsReq\n\t(*Sampler)(nil),           // 11: petstore.Sampler\n\t(*ChangeSamplerReq)(nil),  // 12: petstore.ChangeSamplerReq\n\t(*ChangeSamplerResp)(nil), // 13: petstore.ChangeSamplerResp\n\t(*date.Date)(nil),         // 14: google.type.Date\n}\nvar file_petstore_proto_depIdxs = []int32{\n\t14, // 0: petstore.DateRange.start:type_name -> google.type.Date\n\t14, // 1: petstore.DateRange.end:type_name -> google.type.Date\n\t0,  // 2: petstore.Pet.type:type_name -> petstore.PetType\n\t14, // 3: petstore.Pet.birthday:type_name -> google.type.Date\n\t3,  // 4: petstore.AddPetsReq.pets:type_name -> petstore.Pet\n\t3,  // 5: petstore.UpdatePetsReq.pets:type_name -> petstore.Pet\n\t0,  // 6: petstore.SearchPetsReq.types:type_name -> petstore.PetType\n\t2,  // 7: petstore.SearchPetsReq.birthdate_range:type_name -> petstore.DateRange\n\t1,  // 8: petstore.Sampler.type:type_name -> petstore.SamplerType\n\t11, // 9: petstore.ChangeSamplerReq.sampler:type_name -> petstore.Sampler\n\t4,  // 10: petstore.PetStore.AddPets:input_type -> petstore.AddPetsReq\n\t6,  // 11: petstore.PetStore.UpdatePets:input_type -> petstore.UpdatePetsReq\n\t8,  // 12: petstore.PetStore.DeletePets:input_type -> petstore.DeletePetsReq\n\t10, // 13: petstore.PetStore.SearchPets:input_type -> petstore.SearchPetsReq\n\t12, // 14: petstore.PetStore.ChangeSampler:input_type -> petstore.ChangeSamplerReq\n\t5,  // 15: petstore.PetStore.AddPets:output_type -> petstore.AddPetsResp\n\t7,  // 16: petstore.PetStore.UpdatePets:output_type -> petstore.UpdatePetsResp\n\t9,  // 17: petstore.PetStore.DeletePets:output_type -> petstore.DeletePetsResp\n\t3,  // 18: petstore.PetStore.SearchPets:output_type -> petstore.Pet\n\t13, // 19: petstore.PetStore.ChangeSampler:output_type -> petstore.ChangeSamplerResp\n\t15, // [15:20] is the sub-list for method output_type\n\t10, // [10:15] is the sub-list for method input_type\n\t10, // [10:10] is the sub-list for extension type_name\n\t10, // [10:10] is the sub-list for extension extendee\n\t0,  // [0:10] is the sub-list for field type_name\n}\n\nfunc init() { file_petstore_proto_init() }\nfunc file_petstore_proto_init() {\n\tif File_petstore_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_petstore_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DateRange); 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_petstore_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Pet); 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_petstore_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*AddPetsReq); 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_petstore_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*AddPetsResp); 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_petstore_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*UpdatePetsReq); 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_petstore_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*UpdatePetsResp); 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_petstore_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DeletePetsReq); 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_petstore_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DeletePetsResp); 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_petstore_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*SearchPetsReq); 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_petstore_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Sampler); 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_petstore_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ChangeSamplerReq); 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_petstore_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ChangeSamplerResp); 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_petstore_proto_rawDesc,\n\t\t\tNumEnums:      2,\n\t\t\tNumMessages:   12,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_petstore_proto_goTypes,\n\t\tDependencyIndexes: file_petstore_proto_depIdxs,\n\t\tEnumInfos:         file_petstore_proto_enumTypes,\n\t\tMessageInfos:      file_petstore_proto_msgTypes,\n\t}.Build()\n\tFile_petstore_proto = out.File\n\tfile_petstore_proto_rawDesc = nil\n\tfile_petstore_proto_goTypes = nil\n\tfile_petstore_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "chapter/11/petstore/proto/petstore.proto",
    "content": "syntax = \"proto3\";\n\npackage petstore;\n\noption go_package = \"github.com/PacktPublishing/Go-for-DevOps/proto\";\n\nimport \"google/type/date.proto\";\n\n// Desribes the type of pets.\nenum PetType {\n\t// The type was not set.\n\tPTUnknown = 0;\n\t// The pet is a canine.\n\tPTCanine = 1;\n\t// The pet is a feline.\n\tPTFeline = 2;\n\t// The pet is a bird.\n\tPTBird = 3;\n\t// The pet is a reptile.\n\tPTReptile = 4;\n}\n\n// Represents a range of dates.\nmessage DateRange {\n\t// When to start the range, this is inclusive.\n\tgoogle.type.Date start = 1;\n\t// When to end the range, this is exclusive.\n\tgoogle.type.Date end = 2;\n}\n\n// Represents a unique pet.\nmessage Pet {\n\t// A UUIDv4 for this pet. This can never be set on an AddPet().\n\tstring id = 1;\n\t// The name of the pet.\n\tstring name = 2;\n\t// The type of pet.\n\tPetType type = 3;\n\t// The pet's birthday.\n\tgoogle.type.Date birthday = 4;\n}\n\n// The request used to add a pets to the system.\nmessage AddPetsReq {\n\t// The pet information to add. Pet.id must not be set.\n\trepeated Pet pets = 1;\n}\n\n// The response do AddPets().\nmessage AddPetsResp {\n\t// The IDs of the pets that were added.\n\trepeated string ids = 1;\n}\n\n// The request used to update pets in the system.\nmessage UpdatePetsReq {\n\t// The pet information to update. Pet.id must be set.\n\trepeated Pet pets = 1;\n}\n\n// The response do UpdatePets().\nmessage UpdatePetsResp {}\n\n// Used to indicate which pets to delete. This is an all or nothing request.\nmessage DeletePetsReq {\n\t// The IDs of the pets to delete.\n\trepeated string ids = 1;\n}\n\n// The response to a DeletePet().\nmessage DeletePetsResp{}\n\n// The request to search for pets.\nmessage SearchPetsReq {\n\t// Pet names to filter by.\n\trepeated string names = 1;\n\t// Pet types to filter by.\n\trepeated PetType types = 2;\n\t// Birthdays to filter by.\n\tDateRange birthdate_range = 3;\n}\n\n// Types of OTEL sampling we support.\nenum SamplerType {\n\tSTUnknown = 0;\n\tSTNever = 1;\n\tSTAlways = 2;\n\tSTFloat = 3;\n}\n\nmessage Sampler {\n\t// The type of sampling to change to.\n\tSamplerType type = 1;\n\n\t// This is the sampling rate if type == STFloat. Values must be\n\t// > 0 and <= 1.0 .\t\n\tdouble float_value = 2;\n}\n\n// Used to request we change the OTEL sampling.\nmessage ChangeSamplerReq {\n\tSampler sampler = 1;\n}\n\n// The response to a sampling change.\nmessage ChangeSamplerResp{}\n\nservice PetStore {\n\t// Adds pets to the pet store.\n\trpc AddPets(AddPetsReq) returns (AddPetsResp) {};\n\t// Updates pets entries in the store.\n\trpc UpdatePets(UpdatePetsReq) returns (UpdatePetsResp) {};\n\t// Deletes pets from the pet store.\n\trpc DeletePets(DeletePetsReq) returns (DeletePetsResp) {};\n\t// Finds pets in the pet store.\n\trpc SearchPets(SearchPetsReq) returns (stream Pet) {};\n\n\n\t// These are for management. In real life I might break this into a new server that is\n\t// serving on SSH so that it has to be auth'd and reachable only from in my network\n\t// or only if auth'd by something.\n\n\t// Changes the OTEL sampling type.\n\trpc ChangeSampler(ChangeSamplerReq) returns (ChangeSamplerResp) {};\n}\n"
  },
  {
    "path": "chapter/11/petstore/proto/petstore_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n\npackage proto\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// PetStoreClient is the client API for PetStore 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 PetStoreClient interface {\n\t// Adds pets to the pet store.\n\tAddPets(ctx context.Context, in *AddPetsReq, opts ...grpc.CallOption) (*AddPetsResp, error)\n\t// Updates pets entries in the store.\n\tUpdatePets(ctx context.Context, in *UpdatePetsReq, opts ...grpc.CallOption) (*UpdatePetsResp, error)\n\t// Deletes pets from the pet store.\n\tDeletePets(ctx context.Context, in *DeletePetsReq, opts ...grpc.CallOption) (*DeletePetsResp, error)\n\t// Finds pets in the pet store.\n\tSearchPets(ctx context.Context, in *SearchPetsReq, opts ...grpc.CallOption) (PetStore_SearchPetsClient, error)\n\t// Changes the OTEL sampling type.\n\tChangeSampler(ctx context.Context, in *ChangeSamplerReq, opts ...grpc.CallOption) (*ChangeSamplerResp, error)\n}\n\ntype petStoreClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewPetStoreClient(cc grpc.ClientConnInterface) PetStoreClient {\n\treturn &petStoreClient{cc}\n}\n\nfunc (c *petStoreClient) AddPets(ctx context.Context, in *AddPetsReq, opts ...grpc.CallOption) (*AddPetsResp, error) {\n\tout := new(AddPetsResp)\n\terr := c.cc.Invoke(ctx, \"/petstore.PetStore/AddPets\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *petStoreClient) UpdatePets(ctx context.Context, in *UpdatePetsReq, opts ...grpc.CallOption) (*UpdatePetsResp, error) {\n\tout := new(UpdatePetsResp)\n\terr := c.cc.Invoke(ctx, \"/petstore.PetStore/UpdatePets\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *petStoreClient) DeletePets(ctx context.Context, in *DeletePetsReq, opts ...grpc.CallOption) (*DeletePetsResp, error) {\n\tout := new(DeletePetsResp)\n\terr := c.cc.Invoke(ctx, \"/petstore.PetStore/DeletePets\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *petStoreClient) SearchPets(ctx context.Context, in *SearchPetsReq, opts ...grpc.CallOption) (PetStore_SearchPetsClient, error) {\n\tstream, err := c.cc.NewStream(ctx, &PetStore_ServiceDesc.Streams[0], \"/petstore.PetStore/SearchPets\", opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &petStoreSearchPetsClient{stream}\n\tif err := x.ClientStream.SendMsg(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := x.ClientStream.CloseSend(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn x, nil\n}\n\ntype PetStore_SearchPetsClient interface {\n\tRecv() (*Pet, error)\n\tgrpc.ClientStream\n}\n\ntype petStoreSearchPetsClient struct {\n\tgrpc.ClientStream\n}\n\nfunc (x *petStoreSearchPetsClient) Recv() (*Pet, error) {\n\tm := new(Pet)\n\tif err := x.ClientStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\nfunc (c *petStoreClient) ChangeSampler(ctx context.Context, in *ChangeSamplerReq, opts ...grpc.CallOption) (*ChangeSamplerResp, error) {\n\tout := new(ChangeSamplerResp)\n\terr := c.cc.Invoke(ctx, \"/petstore.PetStore/ChangeSampler\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// PetStoreServer is the server API for PetStore service.\n// All implementations must embed UnimplementedPetStoreServer\n// for forward compatibility\ntype PetStoreServer interface {\n\t// Adds pets to the pet store.\n\tAddPets(context.Context, *AddPetsReq) (*AddPetsResp, error)\n\t// Updates pets entries in the store.\n\tUpdatePets(context.Context, *UpdatePetsReq) (*UpdatePetsResp, error)\n\t// Deletes pets from the pet store.\n\tDeletePets(context.Context, *DeletePetsReq) (*DeletePetsResp, error)\n\t// Finds pets in the pet store.\n\tSearchPets(*SearchPetsReq, PetStore_SearchPetsServer) error\n\t// Changes the OTEL sampling type.\n\tChangeSampler(context.Context, *ChangeSamplerReq) (*ChangeSamplerResp, error)\n\tmustEmbedUnimplementedPetStoreServer()\n}\n\n// UnimplementedPetStoreServer must be embedded to have forward compatible implementations.\ntype UnimplementedPetStoreServer struct {\n}\n\nfunc (UnimplementedPetStoreServer) AddPets(context.Context, *AddPetsReq) (*AddPetsResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method AddPets not implemented\")\n}\nfunc (UnimplementedPetStoreServer) UpdatePets(context.Context, *UpdatePetsReq) (*UpdatePetsResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method UpdatePets not implemented\")\n}\nfunc (UnimplementedPetStoreServer) DeletePets(context.Context, *DeletePetsReq) (*DeletePetsResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method DeletePets not implemented\")\n}\nfunc (UnimplementedPetStoreServer) SearchPets(*SearchPetsReq, PetStore_SearchPetsServer) error {\n\treturn status.Errorf(codes.Unimplemented, \"method SearchPets not implemented\")\n}\nfunc (UnimplementedPetStoreServer) ChangeSampler(context.Context, *ChangeSamplerReq) (*ChangeSamplerResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ChangeSampler not implemented\")\n}\nfunc (UnimplementedPetStoreServer) mustEmbedUnimplementedPetStoreServer() {}\n\n// UnsafePetStoreServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to PetStoreServer will\n// result in compilation errors.\ntype UnsafePetStoreServer interface {\n\tmustEmbedUnimplementedPetStoreServer()\n}\n\nfunc RegisterPetStoreServer(s grpc.ServiceRegistrar, srv PetStoreServer) {\n\ts.RegisterService(&PetStore_ServiceDesc, srv)\n}\n\nfunc _PetStore_AddPets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(AddPetsReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(PetStoreServer).AddPets(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/petstore.PetStore/AddPets\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(PetStoreServer).AddPets(ctx, req.(*AddPetsReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _PetStore_UpdatePets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(UpdatePetsReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(PetStoreServer).UpdatePets(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/petstore.PetStore/UpdatePets\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(PetStoreServer).UpdatePets(ctx, req.(*UpdatePetsReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _PetStore_DeletePets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(DeletePetsReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(PetStoreServer).DeletePets(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/petstore.PetStore/DeletePets\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(PetStoreServer).DeletePets(ctx, req.(*DeletePetsReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _PetStore_SearchPets_Handler(srv interface{}, stream grpc.ServerStream) error {\n\tm := new(SearchPetsReq)\n\tif err := stream.RecvMsg(m); err != nil {\n\t\treturn err\n\t}\n\treturn srv.(PetStoreServer).SearchPets(m, &petStoreSearchPetsServer{stream})\n}\n\ntype PetStore_SearchPetsServer interface {\n\tSend(*Pet) error\n\tgrpc.ServerStream\n}\n\ntype petStoreSearchPetsServer struct {\n\tgrpc.ServerStream\n}\n\nfunc (x *petStoreSearchPetsServer) Send(m *Pet) error {\n\treturn x.ServerStream.SendMsg(m)\n}\n\nfunc _PetStore_ChangeSampler_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ChangeSamplerReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(PetStoreServer).ChangeSampler(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/petstore.PetStore/ChangeSampler\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(PetStoreServer).ChangeSampler(ctx, req.(*ChangeSamplerReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// PetStore_ServiceDesc is the grpc.ServiceDesc for PetStore 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 PetStore_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"petstore.PetStore\",\n\tHandlerType: (*PetStoreServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"AddPets\",\n\t\t\tHandler:    _PetStore_AddPets_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"UpdatePets\",\n\t\t\tHandler:    _PetStore_UpdatePets_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"DeletePets\",\n\t\t\tHandler:    _PetStore_DeletePets_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ChangeSampler\",\n\t\t\tHandler:    _PetStore_ChangeSampler_Handler,\n\t\t},\n\t},\n\tStreams: []grpc.StreamDesc{\n\t\t{\n\t\t\tStreamName:    \"SearchPets\",\n\t\t\tHandler:       _PetStore_SearchPets_Handler,\n\t\t\tServerStreams: true,\n\t\t},\n\t},\n\tMetadata: \"petstore.proto\",\n}\n"
  },
  {
    "path": "chapter/11/prometheus.yaml",
    "content": "scrape_configs:\n  - job_name: 'otel-collector'\n    scrape_interval: 10s\n    static_configs:\n      - targets: ['otel-collector:8889']\n      - targets: ['otel-collector:8888']\n"
  },
  {
    "path": "chapter/12/agent.service",
    "content": "[Unit]\nDescription=System agent for running software\nAfter=network.target\n\n[Service]\nType=simple\nUser=agent\nGroup=agent\n\nRestart=on-failure\nRestartSec=5s\n\nExecStart=/home/agent/bin/agent\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "chapter/12/goss/allfiles/allfiles.go",
    "content": "//go:build linux || darwin\n\n/*\n\t^\n\t|\nIf you haven't see the above line, this is called a build constraint. This will\nonly build for linux or darwin (osx). You can add other build contstraints for architectures or make it so that there are multiple build constraints.\nYou may also use this to have only certain files that are compiled for certain platforms. This allows you to compile in OS/Arch specific instructions\nfor each platform you support. This is how the standard library \"sys\" package works, there is a different \"sys\" for each platform.\n\nWe are restricting this due to the need to read file permissions that we are only sure of on linux and darwin.\n*/\n\npackage main\n\nimport (\n\t\"crypto/sha256\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"log\"\n\t\"os\"\n\t\"os/user\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"syscall\"\n\n\t\"github.com/aelsabbahy/goss\"\n\t\"github.com/aelsabbahy/goss/resource\"\n\t\"gopkg.in/yaml.v2\"\n)\n\nfunc main() {\n\tif len(os.Args) != 2 {\n\t\tfmt.Println(\"must have single argument, the root path to walk\")\n\t\tos.Exit(1)\n\t}\n\tif !strings.HasPrefix(os.Args[1], \"/\") {\n\t\tfmt.Println(\"path argument must be fully qualified\")\n\t\tos.Exit(1)\n\t}\n\tconf := goss.GossConfig{\n\t\tFiles: resource.FileMap{},\n\t}\n\n\tinput := make(chan fileEntry, 1)\n\n\tgo func() {\n\t\tdefer close(input)\n\t\terr := filepath.WalkDir(\n\t\t\tos.Args[1],\n\t\t\tfunc(path string, d fs.DirEntry, err error) error {\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif d.IsDir() {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\tinfo, err := d.Info()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tinput <- fileEntry{p: path, info: info}\n\t\t\t\treturn nil\n\t\t\t},\n\t\t)\n\t\tif err != nil {\n\t\t\tfmt.Println(err)\n\t\t\tos.Exit(1)\n\t\t}\n\t}()\n\n\tlimit := make(chan struct{}, 1000)\n\twg := sync.WaitGroup{}\n\tmu := sync.Mutex{}\n\n\tfor fe := range input {\n\t\tfe := fe\n\t\tlimit <- struct{}{}\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tdefer func() { <-limit }()\n\n\t\t\trsc, err := addFileEntries(fe)\n\t\t\tif err != nil {\n\t\t\t\tlog.Println(\"file could not be added: \", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tmu.Lock()\n\t\t\tconf.Files[fe.p] = rsc\n\t\t\tmu.Unlock()\n\t\t}()\n\t}\n\twg.Wait()\n\n\td, err := yaml.Marshal(conf)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(string(d))\n}\n\ntype fileEntry struct {\n\tp    string\n\tinfo os.FileInfo\n}\n\nfunc addFileEntries(fe fileEntry) (*resource.File, error) {\n\tu, g := mustUserGroup(fe)\n\n\th, err := doSHA256(fe.p)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &resource.File{\n\t\tPath:     fe.p,\n\t\tExists:   true,\n\t\tMode:     fmt.Sprintf(\"%04o\", fe.info.Mode()),\n\t\tOwner:    u,\n\t\tGroup:    g,\n\t\tFiletype: \"file\",\n\t\tSha256:   h,\n\t}, nil\n}\n\nfunc mustUserGroup(fe fileEntry) (string, string) {\n\tstat := fe.info.Sys().(*syscall.Stat_t)\n\tuid := stat.Uid\n\tgid := stat.Gid\n\tu := strconv.FormatUint(uint64(uid), 10)\n\tg := strconv.FormatUint(uint64(gid), 10)\n\tusr, err := user.LookupId(u)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"file(%s) had UID(%d) we couldn't fine: %s\", fe.p, uid, err))\n\t}\n\tgroup, err := user.LookupGroupId(g)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"file(%s) had GID(%d) we couldn't fine: %s\", fe.p, gid, err))\n\t}\n\treturn usr.Username, group.Name\n}\n\nfunc doSHA256(p string) (string, error) {\n\tdst := sha256.New()\n\n\tsrc, err := os.Open(p)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"could not open file: %s\", p)\n\t}\n\tdefer src.Close()\n\n\tif _, err := io.Copy(dst, src); err != nil {\n\t\tpanic(fmt.Sprintf(\"problem doing an SHA256(%s): %s\", p, err))\n\t}\n\n\treturn fmt.Sprintf(\"%x\", dst.Sum(nil)), nil\n}\n"
  },
  {
    "path": "chapter/12/packer/README",
    "content": "These hcl files work, however you will need to put in your own user's access key and secret.\n"
  },
  {
    "path": "chapter/12/packer/amazon.final.pkr.hcl",
    "content": "packer {\n  required_plugins {\n    amazon = {\n      version = \">= 0.0.1\"\n      source  = \"github.com/hashicorp/amazon\"\n    }\n  }\n}\n\nsource \"amazon-ebs\" \"ubuntu\" {\n  access_key    = \"your user's access key\"\n  secret_key    = \"[your secret]\"\n  ami_name      = \"ubuntu-amd64-final\"\n  instance_type = \"t2.micro\"\n  region        = \"us-east-2\"\n  source_ami_filter {\n    filters = {\n      name                = \"ubuntu/images/*ubuntu-xenial-16.04-amd64-server-*\"\n      root-device-type    = \"ebs\"\n      virtualization-type = \"hvm\"\n    }\n    most_recent = true\n    owners      = [\"099720109477\"]\n  }\n  ssh_username = \"ubuntu\"\n}\n\nbuild {\n  name = \"goBook\"\n  sources = [\n    \"source.amazon-ebs.ubuntu\"\n  ]\n  // Install Go 1.17.5\n  provisioner \"shell\" {\n    environment_vars = [\n      \"DEBIAN_FRONTEND=noninteractive\",\n    ]\n    inline = [\n      \"cd ~\",\n      \"mkdir tmp\",\n      \"cd tmp\",\n      \"wget https://golang.org/dl/go1.17.5.linux-amd64.tar.gz\",\n      \"sudo tar -C /usr/local -xzf go1.17.5.linux-amd64.tar.gz\",\n      \"echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.profile\",\n      \". ~/.profile\",\n      \"go version\",\n      \"cd ~/\",\n      \"rm -rf tmp/*\",\n      \"rmdir tmp\",\n    ]\n  }\n  // Setup user \"agent\" with SSH key file\n  provisioner \"shell\" {\n    inline = [\n      \"sudo adduser --disabled-password --gecos '' agent\",\n    ]\n  }\n  provisioner \"file\" {\n    source      = \"./files/agent.pub\"\n    destination = \"/tmp/agent.pub\"\n  }\n  provisioner \"shell\" {\n    inline = [\n      \"sudo mkdir /home/agent/.ssh\",\n      \"sudo mv /tmp/agent.pub /home/agent/.ssh/authorized_keys\",\n      \"sudo chown agent:agent /home/agent/.ssh\",\n      \"sudo chown agent:agent /home/agent/.ssh/authorized_keys\",\n      \"sudo chmod 400 .ssh/authorized_keys\",\n    ]\n  }\n\n  // Setup agent binary running with systemd file.\n  provisioner \"shell\" { // This installs dbus-launch\n    environment_vars = [\n      \"DEBIAN_FRONTEND=noninteractive\",\n    ]\n    inline = [\n      \"sudo apt-get install -y dbus\",\n      \"sudo apt-get install -y dbus-x11\",\n    ]\n  }\n  provisioner \"file\" {\n    source      = \"./files/agent\"\n    destination = \"/tmp/agent\"\n  }\n\n  provisioner \"shell\" {\n    inline = [\n      \"sudo mkdir /home/agent/bin\",\n      \"sudo chown agent:agent /home/agent/bin\",\n      “sudo chmod ug+rwx /home/agent/bin”,\n      \"sudo mv /tmp/agent /home/agent/bin/agent\",\n      \"sudo chown agent:agent /home/agent/bin/agent\",\n      \"sudo chmod 0770 /home/agent/bin/agent\",\n    ]\n  }\n\n  provisioner \"file\" {\n    source      = \"./files/agent.service\"\n    destination = \"/tmp/agent.service\"\n  }\n\n  provisioner \"shell\" {\n    inline = [\n      \"sudo mv /tmp/agent.service /etc/systemd/system/agent.service\",\n      \"sudo systemctl enable agent.service\",\n      \"sudo systemctl daemon-reload\",\n      \"sudo systemctl start agent.service\",\n      \"sleep 10\",\n      \"sudo systemctl is-enabled agent.service\",\n    ]\n  }\n\n  // Setup Goss tool on the image.\n  provisioner \"shell\" {\n    inline = [\n      \"cd ~\",\n      \"mkdir tmp\",\n      \"cd tmp\",\n      \"sudo curl -L https://github.com/aelsabbahy/goss/releases/latest/download/goss-linux-amd64 -o /usr/local/bin/goss\",\n      \"sudo chmod +rx /usr/local/bin/goss\",\n      \"goss -v\",\n      \"cd ~/\",\n      \"rm -rf tmp/*\",\n      \"rmdir tmp\",\n    ]\n  }\n\n  // Copy goss for validating our image onto the image.\n  provisioner \"file\" {\n    source      = \"./files/goss\"\n    destination = \"/home/ubuntu/goss\"\n  }\n\n  // Run the Goss tool using our validation files.\n  provisioner \"goss\" {\n    retry_timeout = \"30s\"\n    tests = [\n      \"files/goss/goss.yaml\",\n      \"files/goss/files.yaml\",\n      \"files/goss/dbus.yaml\",\n      \"files/goss/process.yaml\",\n    ]\n  }\n}\n"
  },
  {
    "path": "chapter/12/packer/amazon.goenv.pkr.hcl",
    "content": "packer {\n  required_plugins {\n    amazon = {\n      version = \">= 0.0.1\"\n      source  = \"github.com/hashicorp/amazon\"\n    }\n    goenv = {\n      version = \">= 0.0.14\"\n      source  = \"github.com/johnsiilver/goenv\"\n    }\n  }\n}\n\nsource \"amazon-ebs\" \"ubuntu\" {\n  access_key    = \"your user's access key\"\n  secret_key    = \"your secret\"\n  ami_name      = \"ubuntu-amd64-goenv4\"\n  instance_type = \"t2.micro\"\n  region        = \"us-east-2\"\n  source_ami_filter {\n    filters = {\n      name                = \"ubuntu/images/*ubuntu-xenial-16.04-amd64-server-*\"\n      root-device-type    = \"ebs\"\n      virtualization-type = \"hvm\"\n    }\n    most_recent = true\n    owners      = [\"099720109477\"]\n  }\n  ssh_username = \"ubuntu\"\n}\n\nbuild {\n  name = \"goBook\"\n  sources = [\n    \"source.amazon-ebs.ubuntu\"\n  ]\n  // Install Go 1.17.5\n  provisioner \"goenv\" {\n    version = \"1.17.5\"\n  }\n  // Setup user \"agent\" with SSH key file\n  provisioner \"shell\" {\n    inline = [\n      \"sudo adduser --disabled-password --gecos '' agent\",\n    ]\n  }\n  provisioner \"file\" {\n    source      = \"./files/agent.pub\"\n    destination = \"/tmp/agent.pub\"\n  }\n  provisioner \"shell\" {\n    inline = [\n      \"sudo mkdir /home/agent/.ssh\",\n      \"sudo mv /tmp/agent.pub /home/agent/.ssh/authorized_keys\",\n      \"sudo chown agent:agent /home/agent/.ssh\",\n      \"sudo chown agent:agent /home/agent/.ssh/authorized_keys\",\n      \"sudo chmod 400 .ssh/authorized_keys\",\n    ]\n  }\n\n  // Setup agent binary running with systemd file.\n  provisioner \"shell\" { // This installs dbus-launch\n    environment_vars = [\n      \"DEBIAN_FRONTEND=noninteractive\",\n    ]\n    inline = [\n      \"sudo apt-get install -y dbus\",\n      \"sudo apt-get install -y dbus-x11\",\n    ]\n  }\n  provisioner \"file\" {\n    source      = \"./files/agent\"\n    destination = \"/tmp/agent\"\n  }\n\n  provisioner \"shell\" {\n    inline = [\n      \"sudo mkdir /home/agent/bin\",\n      \"sudo chown agent:agent /home/agent/bin\",\n      “sudo chmod ug+rwx /home/agent/bin”,\n      \"sudo mv /tmp/agent /home/agent/bin/agent\",\n      \"sudo chown agent:agent /home/agent/bin/agent\",\n      \"sudo chmod 0770 /home/agent/bin/agent\",\n    ]\n  }\n\n  provisioner \"file\" {\n    source      = \"./files/agent.service\"\n    destination = \"/tmp/agent.service\"\n  }\n\n  provisioner \"shell\" {\n    inline = [\n      \"sudo mv /tmp/agent.service /etc/systemd/system/agent.service\",\n      \"sudo systemctl enable agent.service\",\n      \"sudo systemctl daemon-reload\",\n      \"sudo systemctl start agent.service\",\n      \"sleep 10\",\n      \"sudo systemctl is-enabled agent.service\",\n    ]\n  }\n\n  // Setup Goss tool.\n  provisioner \"shell\" {\n    inline = [\n      \"cd ~\",\n      \"mkdir tmp\",\n      \"cd tmp\",\n      \"sudo curl -L https://github.com/aelsabbahy/goss/releases/latest/download/goss-linux-amd64 -o /usr/local/bin/goss\",\n      \"sudo chmod +rx /usr/local/bin/goss\",\n      \"goss -v\",\n      \"cd ~/\",\n      \"rm -rf tmp/*\",\n      \"rmdir tmp\",\n    ]\n  }\n\n  // Setup goss for validating an image.\n  provisioner \"file\" {\n    source      = \"./files/goss\"\n    destination = \"/home/ubuntu/goss\"\n  }\n\n  provisioner \"goss\" {\n    retry_timeout = \"30s\"\n    tests = [\n      \"files/goss/goss.yaml\",\n      \"files/goss/files.yaml\",\n      \"files/goss/dbus.yaml\",\n      \"files/goss/process.yaml\",\n    ]\n  }\n}\n"
  },
  {
    "path": "chapter/12/packer/plugins/goenv/GNUmakefile",
    "content": "NAME=scaffolding\nBINARY=packer-plugin-${NAME}\n\nCOUNT?=1\nTEST?=$(shell go list ./...)\nHASHICORP_PACKER_PLUGIN_SDK_VERSION?=$(shell go list -m github.com/hashicorp/packer-plugin-sdk | cut -d \" \" -f2)\n\n.PHONY: dev\n\nbuild:\n\t@go build -o ${BINARY}\n\ndev: build\n\t@mkdir -p ~/.packer.d/plugins/\n\t@mv ${BINARY} ~/.packer.d/plugins/${BINARY}\n\ntest:\n\t@go test -race -count $(COUNT) $(TEST) -timeout=3m\n\ninstall-packer-sdc: ## Install packer sofware development command\n\t@go install github.com/hashicorp/packer-plugin-sdk/cmd/packer-sdc@${HASHICORP_PACKER_PLUGIN_SDK_VERSION}\n\nci-release-docs: install-packer-sdc\n\t@packer-sdc renderdocs -src docs -partials docs-partials/ -dst docs/\n\t@/bin/sh -c \"[ -d docs ] && zip -r docs.zip docs/\"\n\nplugin-check: install-packer-sdc build\n\t@packer-sdc plugin-check ${BINARY}\n\ntestacc: dev\n\t@PACKER_ACC=1 go test -count $(COUNT) -v $(TEST) -timeout=120m\n\ngenerate: install-packer-sdc\n\t@go generate ./...\n\tpacker-sdc renderdocs -src ./docs -dst ./.docs -partials ./docs-partials\n\t# checkout the .docs folder for a preview of the docs\n"
  },
  {
    "path": "chapter/12/packer/plugins/goenv/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 John Doak\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"
  },
  {
    "path": "chapter/12/packer/plugins/goenv/goenv.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"runtime\"\n\t\"strings\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/12/packer/plugins/goenv/internal/config\"\n\n\t\"github.com/gopherfs/fs/io/mem/simple\"\n\t\"github.com/hashicorp/hcl/v2/hcldec\"\n\t\"github.com/hashicorp/packer-plugin-sdk/packer\"\n\t\"github.com/hashicorp/packer-plugin-sdk/plugin\"\n\t\"github.com/hashicorp/packer-plugin-sdk/version\"\n\n\tpackerConfig \"github.com/hashicorp/packer-plugin-sdk/template/config\"\n)\n\nconst (\n\tver     = \"0.0.15\"\n\trelease = \"dev\"\n)\n\nvar pv *version.PluginVersion\n\nfunc init() {\n\tpv = version.InitializePluginVersion(ver, release)\n}\n\nfunc main() {\n\tset := plugin.NewSet()\n\tset.SetVersion(pv)\n\n\tset.RegisterProvisioner(\"goenv\", &Provisioner{})\n\terr := set.Run()\n\tif err != nil {\n\t\tfmt.Fprintln(os.Stderr, err.Error())\n\t\tos.Exit(1)\n\t}\n}\n\n// Provisioner implements packer.Provisioner.\ntype Provisioner struct {\n\tpacker.Provisioner // Embed the interface.\n\n\tconf     *config.Provisioner\n\tcontent  []byte\n\tfileName string\n}\n\nfunc (p *Provisioner) ConfigSpec() hcldec.ObjectSpec {\n\treturn new(config.FlatProvisioner).HCL2Spec()\n}\n\nfunc (p *Provisioner) Prepare(raws ...interface{}) error {\n\tc := config.Provisioner{}\n\tif err := packerConfig.Decode(&c, nil, raws...); err != nil {\n\t\treturn err\n\t}\n\tc.Defaults()\n\tp.conf = &c\n\treturn nil\n}\n\nfunc (p *Provisioner) Provision(ctx context.Context, u packer.Ui, c packer.Communicator, m map[string]interface{}) error {\n\tu.Message(\"Begin Go environment install\")\n\tif err := p.fetch(ctx, u, c); err != nil {\n\t\tu.Error(fmt.Sprintf(\"Error: %s\", err))\n\t\treturn err\n\t}\n\tif err := p.push(ctx, u, c); err != nil {\n\t\tu.Error(fmt.Sprintf(\"Error: %s\", err))\n\t\treturn err\n\t}\n\tif err := p.unpack(ctx, u, c); err != nil {\n\t\tu.Error(fmt.Sprintf(\"Error: %s\", err))\n\t\treturn err\n\t}\n\tif err := p.test(ctx, u, c); err != nil {\n\t\tu.Error(fmt.Sprintf(\"Error: %s\", err))\n\t\treturn err\n\t}\n\tu.Message(\"Go environment install finished\")\n\treturn nil\n}\n\nfunc (p *Provisioner) fetch(ctx context.Context, u packer.Ui, c packer.Communicator) error {\n\tconst (\n\t\tgoURL = `https://golang.org/dl/go%s.linux-%s.tar.gz`\n\t\tname  = `go%s.linux-%s.tar.gz`\n\t)\n\n\tplatform := runtime.GOARCH\n\n\tif p.conf.Version == \"latest\" {\n\t\tu.Message(\"Determining latest Go version\")\n\t\tresp, err := http.Get(\"https://golang.org/VERSION?m=text\")\n\t\tif err != nil {\n\t\t\tu.Error(\"http get problem: \" + err.Error())\n\t\t\treturn fmt.Errorf(\"problem asking Google for latest Go version: %s\", err)\n\t\t}\n\t\tver, err := io.ReadAll(resp.Body)\n\t\tif err != nil {\n\t\t\tu.Error(\"io read problem: \" + err.Error())\n\t\t\treturn fmt.Errorf(\"problem reading latest Go version: %s\", err)\n\t\t}\n\n\t\tp.conf.Version = strings.TrimPrefix(string(ver), \"go\")\n\t\tu.Message(\"Latest Go version: \" + p.conf.Version)\n\t} else {\n\t\tu.Message(\"Go version to use is: \" + p.conf.Version)\n\t}\n\n\turl := fmt.Sprintf(goURL, p.conf.Version, platform)\n\n\tu.Message(\"Downloading Go version: \" + url)\n\tresp, err := http.Get(url)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"problem reaching golang.org for version(%s): %s)\", p.conf.Version, err)\n\t}\n\tdefer resp.Body.Close()\n\n\tp.content, err = io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"problem downloading file: %s\", err)\n\t}\n\tp.fileName = fmt.Sprintf(name, p.conf.Version, platform)\n\tu.Message(\"Downloading complete\")\n\n\treturn nil\n}\n\nfunc (p *Provisioner) push(ctx context.Context, u packer.Ui, c packer.Communicator) error {\n\tu.Message(\"Pushing Go tarball\")\n\n\tfs := simple.New()\n\tfs.WriteFile(\"/tarball\", p.content, 0700)\n\tfi, _ := fs.Stat(\"/tarball\")\n\n\terr := c.Upload(\n\t\t\"/tmp/\"+p.fileName,\n\t\tbytes.NewReader(p.content),\n\t\t&fi,\n\t)\n\tif err != nil {\n\t\treturn err\n\t}\n\tu.Message(\"Go tarball delivered to: /tmp/\" + p.fileName)\n\treturn nil\n}\n\nfunc (p *Provisioner) unpack(ctx context.Context, u packer.Ui, c packer.Communicator) error {\n\tconst cmd = `sudo tar -C /usr/local -xzf /tmp/%s`\n\tu.Message(\"Unpacking Go tarball to /usr/local\")\n\n\tb := bytes.Buffer{}\n\trc := &packer.RemoteCmd{\n\t\tCommand: fmt.Sprintf(cmd, p.fileName),\n\t\tStdout:  &b,\n\t\tStderr:  &b,\n\t}\n\n\tif err := c.Start(ctx, rc); err != nil {\n\t\treturn fmt.Errorf(\"problem unpacking tarball(%s):\\n%s\", err, b.String())\n\t}\n\tu.Message(\"Unpacked Go tarball\")\n\treturn nil\n}\n\nfunc (p *Provisioner) test(ctx context.Context, u packer.Ui, c packer.Communicator) error {\n\tu.Message(\"Testing Go install\")\n\n\tb := bytes.Buffer{}\n\trc := &packer.RemoteCmd{\n\t\tCommand: `/usr/local/go/bin/go version`,\n\t\tStdout:  &b,\n\t\tStderr:  &b,\n\t}\n\tif err := c.Start(ctx, rc); err != nil {\n\t\treturn fmt.Errorf(\"problem testing Go install(%s):\\n%s\", err, b.String())\n\t}\n\tu.Message(\"Go installed successfully\")\n\treturn nil\n}\n"
  },
  {
    "path": "chapter/12/packer/plugins/goenv/internal/config/config.go",
    "content": "package config\n\n//go:generate packer-sdc mapstructure-to-hcl2 -type Provisioner\n\n// Provisioner is our provisioner configuration.\ntype Provisioner struct {\n\tVersion string\n}\n\n// Default inputs default values.\nfunc (p *Provisioner) Defaults() {\n\tif p.Version == \"\" {\n\t\tp.Version = \"latest\"\n\t}\n}\n"
  },
  {
    "path": "chapter/12/packer/plugins/goenv/internal/config/config.hcl2spec.go",
    "content": "// Code generated by \"packer-sdc mapstructure-to-hcl2\"; DO NOT EDIT.\n\npackage config\n\nimport (\n\t\"github.com/hashicorp/hcl/v2/hcldec\"\n\t\"github.com/zclconf/go-cty/cty\"\n)\n\n// FlatProvisioner is an auto-generated flat version of Provisioner.\n// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.\ntype FlatProvisioner struct {\n\tVersion *string `cty:\"version\" hcl:\"version\"`\n}\n\n// FlatMapstructure returns a new FlatProvisioner.\n// FlatProvisioner is an auto-generated flat version of Provisioner.\n// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.\nfunc (*Provisioner) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {\n\treturn new(FlatProvisioner)\n}\n\n// HCL2Spec returns the hcl spec of a Provisioner.\n// This spec is used by HCL to read the fields of Provisioner.\n// The decoded values from this spec will then be applied to a FlatProvisioner.\nfunc (*FlatProvisioner) HCL2Spec() map[string]hcldec.Spec {\n\ts := map[string]hcldec.Spec{\n\t\t\"version\": &hcldec.AttrSpec{Name: \"version\", Type: cty.String, Required: false},\n\t}\n\treturn s\n}\n"
  },
  {
    "path": "chapter/13/petstore-provider/.gitignore",
    "content": "bin\n.terraform*\n*.tfstate*\n"
  },
  {
    "path": "chapter/13/petstore-provider/Makefile",
    "content": "HOSTNAME=example.com\nNAMESPACE=gofordevops\nNAME=petstore\nBINARY=terraform-provider-${NAME}\nVERSION=0.1.0\nGOARCH  := $(shell go env GOARCH)\nGOOS := $(shell go env GOOS)\n\ndefault: install\n\nbuild:\n\tgo build -o ${BINARY}\n\ninstall: build\n\tmkdir -p ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${GOOS}_${GOARCH}\n\tmv ${BINARY} ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${GOOS}_${GOARCH}\n\ntest:\n\tgo test ./... -v\n\ntestacc:\n\tTF_ACC=1 go test ./... -v $(TESTARGS) -timeout 120m\n"
  },
  {
    "path": "chapter/13/petstore-provider/docker-compose.yml",
    "content": "version: '3.7'\nservices:\n  petstore:\n    build:\n      context: ../../11/petstore/.\n    command:\n      - /go/bin/petstore\n      - --localDebug\n    ports:\n      - \"6742:6742\"\n"
  },
  {
    "path": "chapter/13/petstore-provider/examples/main.tf",
    "content": "terraform {\n  required_providers {\n    petstore = {\n      version = \"0.1.0\"\n      source  = \"example.com/gofordevops/petstore\"\n    }\n  }\n}\n\nprovider \"petstore\" {\n  host = \"127.0.0.1:6742\"\n}\n\nresource \"petstore_pet\" \"thor\" {\n  name     = \"Thor\"\n  type     = \"dog\"\n  birthday = \"2021-04-01T00:00:00Z\"\n}\n\nresource \"petstore_pet\" \"tron\" {\n  name     = \"Tron\"\n  type     = \"cat\"\n  birthday = \"2020-06-25T00:00:00Z\"\n}\n\nvariable \"pet_name\" {\n  type    = string\n  default = \"Thor\"\n}\n\ndata \"petstore_pet\" \"all\" {\n  depends_on = [petstore_pet.thor, petstore_pet.tron]\n}\n\n# Returns all pets\noutput \"all_pets\" {\n  value = data.petstore_pet.all\n}\n\n# Only returns Thor by name\noutput \"thor\" {\n  value = {\n    for pet in data.petstore_pet.all.pets :\n    pet.id => pet\n    if pet.name == var.pet_name\n  }\n}\n"
  },
  {
    "path": "chapter/13/petstore-provider/go.mod",
    "content": "module github.com/PacktPublishing/Go-for-DevOps/chapter/13/petstore-provider\n\ngo 1.17\n\nrequire (\n\tgithub.com/biogo/store v0.0.0-20201120204734-aad293a2328f\n\tgithub.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320\n\tgithub.com/hashicorp/terraform-plugin-sdk/v2 v2.11.0\n\tgithub.com/kylelemons/godebug v1.1.0\n\tgithub.com/pkg/errors v0.9.1\n\tgo.opentelemetry.io/otel v1.4.1\n\tgo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.27.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.27.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.1\n\tgo.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.4.1\n\tgo.opentelemetry.io/otel/metric v0.27.0\n\tgo.opentelemetry.io/otel/sdk v1.4.1\n\tgo.opentelemetry.io/otel/sdk/metric v0.27.0\n\tgo.opentelemetry.io/otel/trace v1.4.1\n\tgoogle.golang.org/genproto v0.0.0-20200711021454-869866162049\n\tgoogle.golang.org/grpc v1.45.0\n\tgoogle.golang.org/protobuf v1.27.1\n)\n\nrequire (\n\tgithub.com/agext/levenshtein v1.2.2 // indirect\n\tgithub.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect\n\tgithub.com/cenkalti/backoff/v4 v4.1.2 // indirect\n\tgithub.com/fatih/color v1.7.0 // indirect\n\tgithub.com/go-logr/logr v1.2.3 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgithub.com/google/go-cmp v0.5.7 // indirect\n\tgithub.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect\n\tgithub.com/hashicorp/errwrap v1.0.0 // indirect\n\tgithub.com/hashicorp/go-hclog v1.2.0 // indirect\n\tgithub.com/hashicorp/go-multierror v1.1.1 // indirect\n\tgithub.com/hashicorp/go-plugin v1.4.3 // indirect\n\tgithub.com/hashicorp/go-uuid v1.0.2 // indirect\n\tgithub.com/hashicorp/go-version v1.4.0 // indirect\n\tgithub.com/hashicorp/hcl/v2 v2.11.1 // indirect\n\tgithub.com/hashicorp/logutils v1.0.0 // indirect\n\tgithub.com/hashicorp/terraform-plugin-go v0.8.0 // indirect\n\tgithub.com/hashicorp/terraform-plugin-log v0.3.0 // indirect\n\tgithub.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896 // indirect\n\tgithub.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect\n\tgithub.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect\n\tgithub.com/mattn/go-colorable v0.1.4 // indirect\n\tgithub.com/mattn/go-isatty v0.0.10 // indirect\n\tgithub.com/mitchellh/copystructure v1.2.0 // indirect\n\tgithub.com/mitchellh/go-testing-interface v1.14.1 // indirect\n\tgithub.com/mitchellh/go-wordwrap v1.0.0 // indirect\n\tgithub.com/mitchellh/mapstructure v1.4.3 // indirect\n\tgithub.com/mitchellh/reflectwalk v1.0.2 // indirect\n\tgithub.com/oklog/run v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack v4.0.4+incompatible // indirect\n\tgithub.com/vmihailenco/msgpack/v4 v4.3.12 // indirect\n\tgithub.com/vmihailenco/tagparser v0.1.1 // indirect\n\tgithub.com/zclconf/go-cty v1.10.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.6.0 // indirect\n\tgo.opentelemetry.io/otel/internal/metric v0.27.0 // indirect\n\tgo.opentelemetry.io/proto/otlp v0.12.0 // indirect\n\tgolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect\n\tgolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect\n\tgolang.org/x/text v0.3.5 // indirect\n\tgoogle.golang.org/appengine v1.6.6 // indirect\n)\n"
  },
  {
    "path": "chapter/13/petstore-provider/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=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=\ngithub.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=\ngithub.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=\ngithub.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=\ngithub.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=\ngithub.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE=\ngithub.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=\ngithub.com/andybalholm/crlf v0.0.0-20171020200849-670099aa064f/go.mod h1:k8feO4+kXDxro6ErPXBRTJ/ro2mf0SsFG8s7doP9kJE=\ngithub.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=\ngithub.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=\ngithub.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFUye+ZcSR6opIgz9Co7WcDx6ZcY+RjfFHoA0I=\ngithub.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=\ngithub.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=\ngithub.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=\ngithub.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=\ngithub.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=\ngithub.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=\ngithub.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=\ngithub.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=\ngithub.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/biogo/store v0.0.0-20201120204734-aad293a2328f h1:+6okTAeUsUrdQr/qN7fIODzowrjjCrnJDg/gkYqcSXY=\ngithub.com/biogo/store v0.0.0-20201120204734-aad293a2328f/go.mod h1:z52shMwD6SGwRg2iYFjjDwX5Ene4ENTw6HfXraUy/08=\ngithub.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=\ngithub.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\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/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\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/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=\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.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=\ngithub.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=\ngithub.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=\ngithub.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=\ngithub.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=\ngithub.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=\ngithub.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=\ngithub.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=\ngithub.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=\ngithub.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\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.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/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.5.0/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/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\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-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg=\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/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=\ngithub.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI=\ngithub.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs=\ngithub.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=\ngithub.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=\ngithub.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=\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-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM=\ngithub.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=\ngithub.com/hashicorp/go-uuid v1.0.0/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/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=\ngithub.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=\ngithub.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4=\ngithub.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=\ngithub.com/hashicorp/hc-install v0.3.1/go.mod h1:3LCdWcCDS1gaHC9mhHCGbkYfoY6vdsKohGjugbZdZak=\ngithub.com/hashicorp/hcl/v2 v2.11.1 h1:yTyWcXcm9XB0TEkyU/JCRU6rYy4K+mgLtzn2wlrJbcc=\ngithub.com/hashicorp/hcl/v2 v2.11.1/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg=\ngithub.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/terraform-exec v0.16.0/go.mod h1:wB5JHmjxZ/YVNZuv9npAXKmz5pGyxy8PSi0GRR0+YjA=\ngithub.com/hashicorp/terraform-json v0.13.0/go.mod h1:y5OdLBCT+rxbwnpxZs9kGL7R9ExU76+cpdY8zHwoazk=\ngithub.com/hashicorp/terraform-plugin-go v0.8.0 h1:MvY43PcDj9VlBjYifBWCO/6j1wf106xU8d5Tob/WRs0=\ngithub.com/hashicorp/terraform-plugin-go v0.8.0/go.mod h1:E3GuvfX0Pz2Azcl6BegD6t51StXsVZMOYQoGO8mkHM0=\ngithub.com/hashicorp/terraform-plugin-log v0.3.0 h1:NPENNOjaJSVX0f7JJTl4f/2JKRPQ7S2ZN9B4NSqq5kA=\ngithub.com/hashicorp/terraform-plugin-log v0.3.0/go.mod h1:EjueSP/HjlyFAsDqt+okpCPjkT4NDynAe32AeDC4vps=\ngithub.com/hashicorp/terraform-plugin-sdk/v2 v2.11.0 h1:ZcHivbkgrQMHOLm6LBvPev7DTUMKmsC8SNcskgLpOYc=\ngithub.com/hashicorp/terraform-plugin-sdk/v2 v2.11.0/go.mod h1:TPjMXvpPNWagHzYOmVPzzRRIBTuaLVukR+esL08tgzg=\ngithub.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896 h1:1FGtlkJw87UsTMg5s8jrekrHmUPUJaMcu6ELiVhQrNw=\ngithub.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896/go.mod h1:bzBPnUIkI0RxauU8Dqo+2KrZZ28Cf48s8V6IHt3p4co=\ngithub.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 h1:HKLsbzeOsfXmKNpr3GiT18XAblV0BjCbzL8KQAMZGa0=\ngithub.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg=\ngithub.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=\ngithub.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=\ngithub.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=\ngithub.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=\ngithub.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=\ngithub.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=\ngithub.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=\ngithub.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=\ngithub.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\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 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=\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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=\ngithub.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=\ngithub.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=\ngithub.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=\ngithub.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=\ngithub.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=\ngithub.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=\ngithub.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=\ngithub.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=\ngithub.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=\ngithub.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=\ngithub.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=\ngithub.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=\ngithub.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=\ngithub.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=\ngithub.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=\ngithub.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=\ngithub.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce h1:RPclfga2SEJmgMmz2k+Mg7cowZ8yv4Trqw9UsJby758=\ngithub.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce/go.mod h1:uFMI8w+ref4v2r9jz+c9i1IfIttS/OkmLfrk1jne5hs=\ngithub.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=\ngithub.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=\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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=\ngithub.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=\ngithub.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=\ngithub.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=\ngithub.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=\ngithub.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=\ngithub.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U=\ngithub.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=\ngithub.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY=\ngithub.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=\ngithub.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=\ngithub.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngithub.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=\ngithub.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=\ngithub.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=\ngithub.com/zclconf/go-cty v1.9.1/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=\ngithub.com/zclconf/go-cty v1.10.0 h1:mp9ZXQeIcN8kAwuqorjH+Q+njbJKjLrvB2yIh4q7U+0=\ngithub.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=\ngithub.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=\ngo.opentelemetry.io/otel v1.4.0/go.mod h1:jeAqMFKy2uLIxCtKxoFj0FAL5zAPKQagc3+GtBWakzk=\ngo.opentelemetry.io/otel v1.4.1 h1:QbINgGDDcoQUoMJa2mMaWno49lja9sHwp6aoa2n3a4g=\ngo.opentelemetry.io/otel v1.4.1/go.mod h1:StM6F/0fSwpd8dKWDCdRr7uRvEPYdW0hBSlbdTiUde4=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.1/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.6.0 h1:XFcfoo+vwXXwopiS7vzwbaFuPplf5GB+WTjaiQXmz3U=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.6.0/go.mod h1:NEu79Xo32iVb+0gVNV8PMd7GoWqnyDXRlj04yFjqz40=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.27.0 h1:t1aPfMj5oZzv2EaRmdC2QPQg1a7MaBjraOh4Hjwuia8=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.27.0/go.mod h1:aZnoYVx7GIuMROciGC3cjZhYxMD/lKroRJUnFY0afu0=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.27.0 h1:RJURCSrqUjJiCY3GuFCVP2EPKOQLwNXQ4FI3aH2KoHg=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.27.0/go.mod h1:LIc1eCpkU94tPnXxH40ya41Oyxm7sL+oDvxCYPFpnV8=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1 h1:WPpPsAAs8I2rA47v5u0558meKmmwm1Dj99ZbqCV8sZ8=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1/go.mod h1:o5RW5o2pKpJLD5dNTCmjF1DorYwMeFJmb/rKr5sLaa8=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.1 h1:AxqDiGk8CorEXStMDZF5Hz9vo9Z7ZZ+I5m8JRl/ko40=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.1/go.mod h1:c6E4V3/U+miqjs/8l950wggHGL1qzlp0Ypj9xoGrPqo=\ngo.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.4.1 h1:yaXaoJjXaJqRnsfW9HrN7pGb7bzcEn31Rk6yo2LFaWo=\ngo.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.4.1/go.mod h1:BFiGsTMZdqtxufux8ANXuMeRz9dMPVFdJZadUWDFD7o=\ngo.opentelemetry.io/otel/internal/metric v0.27.0 h1:9dAVGAfFiiEq5NVB9FUJ5et+btbDQAUIJehJ+ikyryk=\ngo.opentelemetry.io/otel/internal/metric v0.27.0/go.mod h1:n1CVxRqKqYZtqyTh9U/onvKapPGv7y/rpyOTI+LFNzw=\ngo.opentelemetry.io/otel/metric v0.27.0 h1:HhJPsGhJoKRSegPQILFbODU56NS/L1UE4fS1sC5kIwQ=\ngo.opentelemetry.io/otel/metric v0.27.0/go.mod h1:raXDJ7uP2/Jc0nVZWQjJtzoyssOYWu/+pjZqRzfvZ7g=\ngo.opentelemetry.io/otel/sdk v1.4.0/go.mod h1:71GJPNJh4Qju6zJuYl1CrYtXbrgfau/M9UAggqiy1UE=\ngo.opentelemetry.io/otel/sdk v1.4.1 h1:J7EaW71E0v87qflB4cDolaqq3AcujGrtyIPGQoZOB0Y=\ngo.opentelemetry.io/otel/sdk v1.4.1/go.mod h1:NBwHDgDIBYjwK2WNu1OPgsIc2IJzmBXNnvIJxJc8BpE=\ngo.opentelemetry.io/otel/sdk/metric v0.27.0 h1:CDEu96Js5IP7f4bJ8eimxF09V5hKYmE7CeyKSjmAL1s=\ngo.opentelemetry.io/otel/sdk/metric v0.27.0/go.mod h1:lOgrT5C3ORdbqp2LsDrx+pBj6gbZtQ5Omk27vH3EaW0=\ngo.opentelemetry.io/otel/trace v1.4.0/go.mod h1:uc3eRsqDfWs9R7b92xbQbU42/eTNz4N+gLP8qJCi4aE=\ngo.opentelemetry.io/otel/trace v1.4.1 h1:O+16qcdTrT7zxv2J6GejTPFinSwA++cYerC5iSiF8EQ=\ngo.opentelemetry.io/otel/trace v1.4.1/go.mod h1:iYEVbroFCNut9QkwEczV9vMRPHNKSSwYZjulEtsmhFc=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.opentelemetry.io/proto/otlp v0.12.0 h1:CMJ/3Wp7iOWES+CYLfnBv+DVmPbB+kmy9PJ92XvlR6c=\ngo.opentelemetry.io/proto/otlp v0.12.0/go.mod h1:TsIjwGWIx5VFYv9KGVlOpxoBl5Dy+63SUguV7GGvlSQ=\ngo.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=\ngo.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngolang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/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-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\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-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=\ngolang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=\ngolang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\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-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180811021610-c39426892332/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-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-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191009170851-d66e71096ffb/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-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\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-20190423024810-112230192c58/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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\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-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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191008105621-543471e840be/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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/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-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/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 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=\ngolang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\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-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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200713011307-fd294ab11aed/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\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=\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.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200711021454-869866162049 h1:YFTFpQhgvrLrmxtiIncJxFXeCyq84ixuKWVCaCAi9Oc=\ngoogle.golang.org/genproto v0.0.0-20200711021454-869866162049/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=\ngoogle.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=\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/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/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=\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.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\n"
  },
  {
    "path": "chapter/13/petstore-provider/internal/client/client.go",
    "content": "// Client provides an API client to the petstore service.\npackage client\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/metadata\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/13/petstore-provider/internal/client/internal/server/storage\"\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/13/petstore-provider/internal/client/proto\"\n)\n\n// Client is a client to the petstore service.\ntype Client struct {\n\tclient pb.PetStoreClient\n\tconn   *grpc.ClientConn\n}\n\n// New is the constructor for Client. addr is the server's [host]:[port].\nfunc New(addr string) (*Client, error) {\n\tconn, err := grpc.Dial(addr, grpc.WithInsecure())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Client{\n\t\tclient: pb.NewPetStoreClient(conn),\n\t\tconn:   conn,\n\t}, nil\n}\n\n// Pet is a wrapper around a *pb.Pet that can return Go versions of\n// fields and errors if the returned stream has an error.\ntype Pet struct {\n\t*pb.Pet\n\terr error\n}\n\n// Proto will give the Pet's proto representation.\nfunc (p Pet) Proto() *pb.Pet {\n\treturn p.Pet\n}\n\n// Birthday returns the Pet's birthday as a time.Time.\nfunc (p Pet) Birthday() time.Time {\n\t// We are ignoring the error as we will either get a zero time\n\t// anyways and the server should be preventing this problem.\n\tt, _ := storage.BirthdayToTime(context.Background(), p.Pet.Birthday)\n\treturn t\n}\n\n// Error indicates if there was an error in the Pet output stream.\nfunc (p Pet) Error() error {\n\treturn p.err\n}\n\n// CallOptions are optional options for an RPC call.\ntype CallOption func(co *callOptions)\n\ntype callOptions struct {\n\ttrace *string\n}\n\n// TraceID will cause the RPC call to execute a trace on the service and return \"s\" to the ID.\n// If s == nil, this will ignore the option. If \"s\" is not set after the call finishes, then\n// no trace was made.\nfunc TraceID(s *string) CallOption {\n\treturn func(co *callOptions) {\n\t\tif s == nil {\n\t\t\treturn\n\t\t}\n\t\tco.trace = s\n\t}\n}\n\n// AddPets adds pets to the service and returns their unique identities in the\n// same order as being added.\nfunc (c *Client) AddPets(ctx context.Context, pets []*pb.Pet, options ...CallOption) ([]string, error) {\n\tif len(pets) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tfor _, p := range pets {\n\t\tif err := storage.ValidatePet(ctx, p, false); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tvar header metadata.MD\n\tctx, gOpts, f := handleCallOptions(ctx, &header, options)\n\tdefer f()\n\n\tresp, err := c.client.AddPets(ctx, &pb.AddPetsReq{Pets: pets}, gOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn resp.Ids, nil\n}\n\n// UpdatePets updates pets that already exist in the system.\nfunc (c *Client) UpdatePets(ctx context.Context, pets []*pb.Pet, options ...CallOption) error {\n\tif len(pets) == 0 {\n\t\treturn nil\n\t}\n\n\tfor _, p := range pets {\n\t\tif err := storage.ValidatePet(ctx, p, true); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tvar header metadata.MD\n\tctx, gOpts, f := handleCallOptions(ctx, &header, options)\n\tdefer f()\n\n\t_, err := c.client.UpdatePets(ctx, &pb.UpdatePetsReq{Pets: pets}, gOpts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// DeletePets deletes pets with the IDs passed. If the ID doesn't exist, the\n// system ignores it.\nfunc (c *Client) DeletePets(ctx context.Context, ids []string, options ...CallOption) error {\n\tif len(ids) == 0 {\n\t\treturn nil\n\t}\n\n\tvar header metadata.MD\n\tctx, gOpts, f := handleCallOptions(ctx, &header, options)\n\tdefer f()\n\n\t_, err := c.client.DeletePets(ctx, &pb.DeletePetsReq{Ids: ids}, gOpts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// SearchPets searches the pet store for pets matching the filter. If the filter contains\n// no entries, then all pets will be returned.\nfunc (c *Client) SearchPets(ctx context.Context, filter *pb.SearchPetsReq, options ...CallOption) (chan Pet, error) {\n\tif filter == nil {\n\t\treturn nil, fmt.Errorf(\"the filter cannot be nil\")\n\t}\n\n\tvar header metadata.MD\n\tctx, gOpts, f := handleCallOptions(ctx, &header, options)\n\n\tstream, err := c.client.SearchPets(ctx, filter, gOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tch := make(chan Pet, 1)\n\tgo func() {\n\t\tdefer close(ch)\n\t\tdefer f()\n\n\t\tfor {\n\t\t\tp, err := stream.Recv()\n\t\t\tif err == io.EOF {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\tch <- Pet{err: err}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tch <- Pet{Pet: p}\n\t\t}\n\t}()\n\treturn ch, nil\n}\n\n// SamplerType is the type of OTEL sampling to do.\ntype SamplerType int32\n\nconst (\n\tSTUnknown SamplerType = 0\n\tNever     SamplerType = 1\n\tAlways    SamplerType = 2\n\tFloat     SamplerType = 3\n)\n\nvar validTypes = map[SamplerType]bool{\n\tNever:  true,\n\tAlways: true,\n\tFloat:  true,\n}\n\ntype Sampler struct {\n\t// Type is the type of sampling to use.\n\tType SamplerType\n\t// Rate is the sampling rate, only used if type is Float.\n\tRate float64\n}\n\nfunc (s *Sampler) validate() error {\n\tif !validTypes[s.Type] {\n\t\treturn fmt.Errorf(\"type %v is not a supported type\", s.Type)\n\t}\n\tif s.Type == Float {\n\t\tif s.Rate <= 0 || s.Rate > 1 {\n\t\t\treturn fmt.Errorf(\"Rate must be > 0 && <= 1.0, was %v\", s.Rate)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (s *Sampler) proto() *pb.Sampler {\n\treturn &pb.Sampler{\n\t\tType:       pb.SamplerType(s.Type),\n\t\tFloatValue: s.Rate,\n\t}\n}\n\nfunc (s *Sampler) fromProto(p *pb.Sampler) {\n\ts.Type = SamplerType(p.Type)\n\ts.Rate = p.FloatValue\n}\n\n// ChangeSampler changes the sampling type and rate on the server. This is\n// and admin function that in production should be restricted.\nfunc (c *Client) ChangeSampler(ctx context.Context, sc Sampler, options ...CallOption) error {\n\tif err := sc.validate(); err != nil {\n\t\treturn err\n\t}\n\n\tvar header metadata.MD\n\tctx, gOpts, f := handleCallOptions(ctx, &header, options)\n\tdefer f()\n\n\t_, err := c.client.ChangeSampler(ctx, &pb.ChangeSamplerReq{Sampler: sc.proto()}, gOpts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc handleCallOptions(ctx context.Context, header *metadata.MD, options []CallOption) (context.Context, []grpc.CallOption, func()) {\n\topts := callOptions{}\n\tfor _, o := range options {\n\t\to(&opts)\n\t}\n\tvar gOpts []grpc.CallOption\n\n\tif opts.trace != nil {\n\t\t(*header)[\"trace\"] = nil\n\t\tgOpts = append(gOpts, grpc.Header(header))\n\t}\n\n\tf := func() {\n\t\tif opts.trace != nil {\n\t\t\tif len((*header)[\"otel.traceID\"]) != 0 {\n\t\t\t\t*opts.trace = (*header)[\"otel.traceID\"][0]\n\t\t\t}\n\t\t}\n\t}\n\n\treturn ctx, gOpts, f\n}\n"
  },
  {
    "path": "chapter/13/petstore-provider/internal/client/internal/server/errors/errors.go",
    "content": "// Package errors is a replacement for the golang standard library \"errors\". This replacement\n// adds errors to the Open Telemetry spans. The signatures only differs in that\n// New() now takes a context.Context object and fmt.Errorf() has been moved here and also takes a Context.Context.\npackage errors\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"go.opentelemetry.io/otel/codes\"\n\t\"go.opentelemetry.io/otel/trace\"\n)\n\n// New creates a new error and writes the error to a span if it exists in the context.\nfunc New(ctx context.Context, text string) error {\n\tspan := trace.SpanFromContext(ctx)\n\n\terr := errors.New(text)\n\tspan.RecordError(err)\n\tspan.SetStatus(codes.Error, err.Error())\n\n\treturn err\n}\n\n// Errorf implements fmt.Errorf with the addition of a Context that if it contains a span\n// will have the error added to the span.\nfunc Errorf(ctx context.Context, s string, i ...interface{}) error {\n\tspan := trace.SpanFromContext(ctx)\n\n\terr := fmt.Errorf(s, i...)\n\tspan.RecordError(err)\n\tspan.SetStatus(codes.Error, err.Error())\n\n\treturn err\n}\n\n// As implements errors.As().\nfunc As(err error, target interface{}) bool {\n\treturn errors.As(err, target)\n}\n\n// Is implements errors.Is().\nfunc Is(err, target error) bool {\n\treturn errors.Is(err, target)\n}\n\n// Unwrap implemements errors.Unwrap().\nfunc Unwrap(err error) error {\n\treturn errors.Unwrap(err)\n}\n"
  },
  {
    "path": "chapter/13/petstore-provider/internal/client/internal/server/log/log.go",
    "content": "/*\nPackage log is a replacement for the standard library log package that\nlogs to OTEL spans contained in Context objects. These are seen as\nevents with the attribute \"log\" set to true.\n\nThe preferred way to log is to use an event:\n\tfunc someFunc(ctx context.Context) {\n\t\te := NewEvent(\"someFunc()\")\n\t\tdefer e.Done(ctx)\n\t\tstart := time.Now()\n\t\tdefer func() {\n\t\t\te.Add(\"latency.ns\", int(time.Since(start)))\n\t\t}()\n\t}\n\nThis records an event in the current span that has a key of \"latency.ns\" with the value in nano-seconds the operation took.\n\nYou can use this to log in a similar manner to the logging package with Println and Printf.  This is generally only useful for some generic debugging where you want to log something and filter the trace by messages with key \"log\". Generally these are messages you don't want to keep.\n\tfunc main() {\n\t\tctx := context.Background()\n\n\t\tlog.SetFlags(log.LstdFlags | log.Lshortfile)\n\n\t\tlog.Println(ctx, \"Starting main\")\n\n\t\tlog.Printf(ctx, \"Env variables: %v\", os.Environ())\n\t}\n\nThe above won't log anything, as there is no Span on the Context. If there\nwas it would get output to the Open Telementry provider.\n\nIf you need to use the standard library log, you can use Logger:\n\tlog.Logger.Println(\"hello world\")\n\nThis would print whever the stanard logger prints to. This defaults\nto the standard logger, but you can replace with another Logger if you wish.\n\nYou should only log messages with a standard logger when it can't be output to a trace. These are critical messages that indicate a definite bug. This keeps logging to only critical events and de-clutters what you need to look at to when doing a debug.\n*/\npackage log\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"runtime\"\n\t\"sync\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n)\n\nconst (\n\tLdate         = 1 << iota     // the date in the local time zone: 2009/01/23\n\tLtime                         // the time in the local time zone: 01:23:23\n\tLmicroseconds                 // microsecond resolution: 01:23:23.123123.  assumes Ltime.\n\tLlongfile                     // full file name and line number: /a/b/c/d.go:23\n\tLshortfile                    // final file name element and line number: d.go:23. overrides Llongfile\n\tLUTC                          // if Ldate or Ltime is set, use UTC rather than the local time zone\n\tLstdFlags     = Ldate | Ltime // initial values for the standard logger\n)\n\n// Logger provides access to the standard library's default logger.\n// This can be replaced in main with a logger of your choice.\nvar Logger *log.Logger = log.Default()\n\n// setup the standard logger with flags.\nvar std = &logger{flag: LstdFlags}\n\n// pool provides a pool of Event objects to keep our allocations to a minimum.\nvar pool = &eventPool{\n\tbuf: make(chan *Event, 100),\n\tpool: sync.Pool{\n\t\tNew: func() interface{} {\n\t\t\treturn &Event{}\n\t\t},\n\t},\n}\n\n// eventPool uses a set amount of Event objects and a sync.Pool for overflow.\n// Note: this would actually be a great place for metrics to key in on what would be\n// an optimal size for buf to prevent pool use.\ntype eventPool struct {\n\tbuf  chan *Event\n\tpool sync.Pool\n}\n\nfunc (e *eventPool) get() *Event {\n\tselect {\n\tcase ev := <-e.buf:\n\t\treturn ev\n\tdefault:\n\t}\n\treturn e.pool.Get().(*Event)\n}\n\nfunc (e *eventPool) put(ev *Event) {\n\tev.reset()\n\tselect {\n\tcase e.buf <- ev:\n\tdefault:\n\t}\n\te.pool.Put(ev)\n}\n\n// Event represents a named event that occurs. This is the prefered way to log data.\n// Events have attributes and those attributes are key/value pairs. You create\n// an event and stuff attributes using Add() until the event is over and call Done().\n// This will render the event to the current span. if no attrs exist, the event is ignored.\n// To avoid extra allocations\ntype Event struct {\n\tname  string\n\tattrs []attribute.KeyValue\n}\n\n// NewEvent returns a new Event.\nfunc NewEvent(name string) *Event {\n\tev := pool.get()\n\tev.name = name\n\treturn ev\n}\n\nfunc (e *Event) reset() {\n\te.name = \"\"\n\te.attrs = e.attrs[0:0]\n}\n\n// Add adds an attribute named k with value i. i can be: bool, []bool, float64, []float64, int, []int, int64, []int64, string and []string.\n// If the value isn't one of those values, a standard log message is printed indicating a bug.\nfunc (e *Event) Add(k string, i interface{}) {\n\tif e.name == \"\" {\n\t\treturn\n\t}\n\tswitch v := i.(type) {\n\tcase bool:\n\t\te.attrs = append(e.attrs, attribute.Bool(k, v))\n\tcase []bool:\n\t\te.attrs = append(e.attrs, attribute.BoolSlice(k, v))\n\tcase float64:\n\t\te.attrs = append(e.attrs, attribute.Float64(k, v))\n\tcase []float64:\n\t\te.attrs = append(e.attrs, attribute.Float64Slice(k, v))\n\tcase int:\n\t\te.attrs = append(e.attrs, attribute.Int(k, v))\n\tcase []int:\n\t\te.attrs = append(e.attrs, attribute.IntSlice(k, v))\n\tcase int64:\n\t\te.attrs = append(e.attrs, attribute.Int64(k, v))\n\tcase []int64:\n\t\te.attrs = append(e.attrs, attribute.Int64Slice(k, v))\n\tcase string:\n\t\te.attrs = append(e.attrs, attribute.String(k, v))\n\tcase []string:\n\t\te.attrs = append(e.attrs, attribute.StringSlice(k, v))\n\tdefault:\n\t\tlog.Printf(\"bug: event.Add(): receiveing %T which is not supported\", v)\n\t}\n}\n\n// Done renders the Event to the span in the Context. If there are no attributes on the Event, this is a no-oop.\n// Once Done is called, the Event object MUST not be used again.\nfunc (e *Event) Done(ctx context.Context) {\n\tdefer pool.put(e)\n\n\tif e.name == \"\" {\n\t\treturn\n\t}\n\tspan := trace.SpanFromContext(ctx)\n\tif e.attrs == nil {\n\t\treturn\n\t}\n\tspan.AddEvent(e.name, trace.WithAttributes(e.attrs...))\n}\n\n// Println acts like log.Println() except we log to the OTEL span in the Context.\nfunc Println(ctx context.Context, v ...interface{}) {\n\tspan := trace.SpanFromContext(ctx)\n\tif !span.IsRecording() {\n\t\treturn\n\t}\n\tstd.output(span, 2, fmt.Sprintln(v...))\n}\n\n// Printf acts like log.Printf() except we log to the OTEL span in the Context.\nfunc Printf(ctx context.Context, format string, v ...interface{}) {\n\tspan := trace.SpanFromContext(ctx)\n\tif !span.IsRecording() {\n\t\treturn\n\t}\n\tstd.output(span, 2, fmt.Sprintf(format, v...))\n}\n\n// SetFlags sets the output flags for the standard logger.\nfunc SetFlags(flag int) {\n\tstd.flag = flag\n}\n\n// logger is an implementation of log.Logger that writes to a Span.\ntype logger struct {\n\tmu   sync.Mutex\n\tflag int    // properties\n\tbuf  []byte // for accumulating text to write\n}\n\n// Cheap integer to fixed-width decimal ASCII. Give a negative width to avoid zero-padding.\nfunc itoa(buf *[]byte, i int, wid int) {\n\t// Assemble decimal in reverse order.\n\tvar b [20]byte\n\tbp := len(b) - 1\n\tfor i >= 10 || wid > 1 {\n\t\twid--\n\t\tq := i / 10\n\t\tb[bp] = byte('0' + i - q*10)\n\t\tbp--\n\t\ti = q\n\t}\n\t// i < 10\n\tb[bp] = byte('0' + i)\n\t*buf = append(*buf, b[bp:]...)\n}\n\nfunc (l *logger) output(span trace.Span, calldepth int, s string) error {\n\tnow := time.Now() // get this early\n\tvar file string\n\tvar line int\n\tl.mu.Lock()\n\tdefer l.mu.Unlock()\n\n\tif l.flag&(Lshortfile|Llongfile) != 0 {\n\t\t// Release lock while getting caller info - it's expensive.\n\t\tl.mu.Unlock()\n\t\tvar ok bool\n\t\t_, file, line, ok = runtime.Caller(calldepth)\n\t\tif !ok {\n\t\t\tfile = \"???\"\n\t\t\tline = 0\n\t\t}\n\t\tl.mu.Lock()\n\t}\n\tl.buf = l.buf[:0]\n\tl.formatHeader(&l.buf, now, file, line)\n\tl.buf = append(l.buf, s...)\n\tif len(s) == 0 || s[len(s)-1] != '\\n' {\n\t\tl.buf = append(l.buf, '\\n')\n\t}\n\tspan.AddEvent(string(l.buf), trace.WithAttributes(attribute.Bool(\"log\", true)))\n\treturn nil\n}\n\n// formatHeader writes log header to buf in following order:\n//   * date and/or time (if corresponding flags are provided),\n//   * file and line number (if corresponding flags are provided),\nfunc (l *logger) formatHeader(buf *[]byte, t time.Time, file string, line int) {\n\tif l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {\n\t\tif l.flag&LUTC != 0 {\n\t\t\tt = t.UTC()\n\t\t}\n\t\tif l.flag&Ldate != 0 {\n\t\t\tyear, month, day := t.Date()\n\t\t\titoa(buf, year, 4)\n\t\t\t*buf = append(*buf, '/')\n\t\t\titoa(buf, int(month), 2)\n\t\t\t*buf = append(*buf, '/')\n\t\t\titoa(buf, day, 2)\n\t\t\t*buf = append(*buf, ' ')\n\t\t}\n\t\tif l.flag&(Ltime|Lmicroseconds) != 0 {\n\t\t\thour, min, sec := t.Clock()\n\t\t\titoa(buf, hour, 2)\n\t\t\t*buf = append(*buf, ':')\n\t\t\titoa(buf, min, 2)\n\t\t\t*buf = append(*buf, ':')\n\t\t\titoa(buf, sec, 2)\n\t\t\tif l.flag&Lmicroseconds != 0 {\n\t\t\t\t*buf = append(*buf, '.')\n\t\t\t\titoa(buf, t.Nanosecond()/1e3, 6)\n\t\t\t}\n\t\t\t*buf = append(*buf, ' ')\n\t\t}\n\t}\n\tif l.flag&(Lshortfile|Llongfile) != 0 {\n\t\tif l.flag&Lshortfile != 0 {\n\t\t\tshort := file\n\t\t\tfor i := len(file) - 1; i > 0; i-- {\n\t\t\t\tif file[i] == '/' {\n\t\t\t\t\tshort = file[i+1:]\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tfile = short\n\t\t}\n\t\t*buf = append(*buf, file...)\n\t\t*buf = append(*buf, ':')\n\t\titoa(buf, line, -1)\n\t\t*buf = append(*buf, \": \"...)\n\t}\n}\n"
  },
  {
    "path": "chapter/13/petstore-provider/internal/client/internal/server/storage/mem/mem.go",
    "content": "// Package mem contains an in-memory storage implementation of storage.Data.\n// This is great for unit tests and demos. Our implementation uses a\n// left-leaning red black tree for storage of entries by birthdays and maps\n// for all other indexes. Filtering is done by searching all indexes for matches\n// by each filter and if all matches succeed we stream the entry found.\npackage mem\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/13/petstore-provider/internal/client/internal/server/errors\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/13/petstore-provider/internal/client/internal/server/log\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/13/petstore-provider/internal/client/internal/server/storage\"\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/13/petstore-provider/internal/client/proto\"\n\n\t\"github.com/biogo/store/llrb\"\n)\n\n// birthdays represents a set of pets that share the same birthday with\n// keys that are pet IDs. This is what we insert into our birthday tree.\ntype birthdays map[string]*pb.Pet\n\n// Compare implements the llrb.Comparable.Compare().\nfunc (bi birthdays) Compare(b llrb.Comparable) int {\n\tvar ap, bp *pb.Pet\n\t// Get any entry in the map, all have the same birthday.\n\tfor _, ap = range bi {\n\t\tbreak\n\t}\n\tfor _, bp = range b.(birthdays) {\n\t\tbreak\n\t}\n\n\t// Ignore errors because we have to conform to a function def\n\t// and we should not be storing records with errors in the Birthday.\n\tat, _ := storage.BirthdayToTime(nil, ap.Birthday)\n\tbt, _ := storage.BirthdayToTime(nil, bp.Birthday)\n\n\tswitch {\n\tcase at.Before(bt):\n\t\treturn -1\n\tcase at.Equal(bt):\n\t\treturn 0\n\t}\n\treturn 1\n}\n\n// birthdayGet is what we use to search for a pets with a particular birthday.\ntype birthdayGet struct {\n\t*pb.Pet\n}\n\n// Compare implements the llrb.Comparable.Compare().\nfunc (bi birthdayGet) Compare(b llrb.Comparable) int {\n\t// Ignore errors because we have to conform to a function def\n\t// and we should not be storing records with errors in the Birthday.\n\tat, _ := storage.BirthdayToTime(nil, bi.Pet.Birthday)\n\tvar bt time.Time\n\tswitch v := b.(type) {\n\tcase birthdayGet:\n\t\tbt, _ = storage.BirthdayToTime(nil, v.Pet.Birthday)\n\tcase birthdays:\n\t\tvar p *pb.Pet\n\t\tfor _, p = range v {\n\t\t\tbreak\n\t\t}\n\t\tbt, _ = storage.BirthdayToTime(nil, p.Birthday)\n\t}\n\n\tswitch {\n\tcase at.Before(bt):\n\t\treturn -1\n\tcase at.Equal(bt):\n\t\treturn 0\n\t}\n\treturn 1\n}\n\n// Data implements storage.Data.\ntype Data struct {\n\tmu       sync.RWMutex // protects the items in this block\n\tbirthday *llrb.Tree\n\tnames    map[string]map[string]*pb.Pet\n\tids      map[string]*pb.Pet\n\ttypes    map[pb.PetType]map[string]*pb.Pet\n\n\t// searches contains all the search calls that must be done\n\t// when we do a search. This is populated in New().\n\tsearches []func(context.Context, *pb.SearchPetsReq) []string\n}\n\n// New is the constructor for Data.\nfunc New() *Data {\n\td := Data{\n\t\tnames:    map[string]map[string]*pb.Pet{},\n\t\tids:      map[string]*pb.Pet{},\n\t\tbirthday: &llrb.Tree{},\n\t\ttypes:    map[pb.PetType]map[string]*pb.Pet{},\n\t}\n\td.searches = []func(context.Context, *pb.SearchPetsReq) []string{\n\t\td.byNames,\n\t\td.byTypes,\n\t\td.byBirthdays,\n\t}\n\treturn &d\n}\n\n// AddPets implements storage.Data.AddPets().\nfunc (d *Data) AddPets(ctx context.Context, pets []*pb.Pet) error {\n\te := log.NewEvent(\"mem.data.AddPets()\")\n\tdefer e.Done(ctx)\n\tstart := time.Now()\n\tdefer func() {\n\t\te.Add(\"latency.ns\", time.Since(start))\n\t}()\n\n\td.mu.RLock()\n\t// Make sure that none of these IDs somehow exist already.\n\tfor _, p := range pets {\n\t\tif _, ok := d.ids[p.Id]; ok {\n\t\t\treturn errors.Errorf(ctx, \"pet with ID(%s) is already present\", p.Id)\n\t\t}\n\t}\n\td.mu.RUnlock()\n\n\td.mu.Lock()\n\tdefer d.mu.Unlock()\n\n\td.populate(ctx, pets)\n\treturn nil\n}\n\n// UpdatePets implements storage.Data.AddPets().\nfunc (d *Data) UpdatePets(ctx context.Context, pets []*pb.Pet) error {\n\td.mu.RLock()\n\t// Make sure that ALL of these IDs somehow exist already.\n\tfor _, p := range pets {\n\t\tif _, ok := d.ids[p.Id]; !ok {\n\t\t\treturn errors.Errorf(ctx, \"pet with ID(%s) doesn't exist\", p.Id)\n\t\t}\n\t}\n\td.mu.RUnlock()\n\n\td.mu.Lock()\n\tdefer d.mu.Unlock()\n\n\td.populate(ctx, pets)\n\treturn nil\n}\n\nfunc (d *Data) populate(ctx context.Context, pets []*pb.Pet) {\n\te := log.NewEvent(\"mem.data.populate()\")\n\tdefer e.Done(ctx)\n\n\tfor _, p := range pets {\n\t\td.ids[p.Id] = p\n\t\tif v, ok := d.names[p.Name]; ok {\n\t\t\tv[p.Id] = p\n\t\t} else {\n\t\t\td.names[p.Name] = map[string]*pb.Pet{\n\t\t\t\tp.Id: p,\n\t\t\t}\n\t\t}\n\t\tif v, ok := d.types[p.Type]; ok {\n\t\t\tv[p.Id] = p\n\t\t} else {\n\t\t\td.types[p.Type] = map[string]*pb.Pet{\n\t\t\t\tp.Id: p,\n\t\t\t}\n\t\t}\n\t\tv := d.birthday.Get(birthdayGet{p})\n\t\tif v == nil {\n\t\t\td.birthday.Insert(birthdays{p.Id: p})\n\t\t\tcontinue\n\t\t}\n\t\tv.(birthdays)[p.Id] = p\n\t}\n}\n\n// DeletePets implements stroage.Data.DeletePets().\nfunc (d *Data) DeletePets(ctx context.Context, ids []string) error {\n\te := log.NewEvent(\"mem.data.DeletePets()\")\n\tdefer e.Done(ctx)\n\tstart := time.Now()\n\tdefer func() {\n\t\te.Add(\"latency.ns\", time.Since(start))\n\t}()\n\n\td.mu.Lock()\n\tdefer d.mu.Unlock()\n\n\tfor _, id := range ids {\n\t\tp, ok := d.ids[id]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tdelete(d.ids, id)\n\t\tif v, ok := d.names[p.Name]; ok {\n\t\t\tif len(v) == 1 {\n\t\t\t\tdelete(d.names, p.Name)\n\t\t\t} else {\n\t\t\t\tdelete(v, id)\n\t\t\t}\n\t\t}\n\t\tif v, ok := d.types[p.Type]; ok {\n\t\t\tif len(v) == 1 {\n\t\t\t\tdelete(d.types, p.Type)\n\t\t\t} else {\n\t\t\t\tdelete(v, id)\n\t\t\t}\n\t\t}\n\t\tv := d.birthday.Get(birthdayGet{p})\n\t\tif v == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif len(v.(birthdays)) == 1 {\n\t\t\td.birthday.Delete(birthdayGet{p})\n\t\t}\n\t\tdelete(v.(birthdays), p.Id)\n\t}\n\treturn nil\n}\n\n// SearchPets implements storage.Data.SearchPets().\nfunc (d *Data) SearchPets(ctx context.Context, filter *pb.SearchPetsReq) chan storage.SearchItem {\n\tpetsCh := make(chan storage.SearchItem, 1)\n\n\tgo func() {\n\t\tdefer close(petsCh)\n\t\td.searchPets(ctx, filter, petsCh)\n\t}()\n\n\treturn petsCh\n}\n\nfunc (d *Data) searchPets(ctx context.Context, filter *pb.SearchPetsReq, out chan storage.SearchItem) {\n\te := log.NewEvent(\"mem.data.searchPets()\")\n\tdefer e.Done(ctx)\n\n\td.mu.RLock()\n\tdefer d.mu.RUnlock()\n\n\tfilters := 0\n\tif len(filter.Names) > 0 {\n\t\te.Add(\"filterNames\", true)\n\t\tfilters++\n\t}\n\tif len(filter.Types) > 0 {\n\t\te.Add(\"filterTypes\", true)\n\t\tfilters++\n\t}\n\tif filter.BirthdateRange != nil {\n\t\te.Add(\"filterBirthday\", true)\n\t\tfilters++\n\t}\n\n\t// They didn't provide filters, so just return everything.\n\tif filters == 0 {\n\t\te.Add(\"returnAll\", true)\n\t\td.returnAll(ctx, out)\n\t\treturn\n\t}\n\n\tsearchCh := make(chan []string, len(d.searches))\n\twg := sync.WaitGroup{}\n\twg.Add(len(d.searches))\n\n\tgoCount := 0\n\t// Spin off our searches.\n\tfor _, search := range d.searches {\n\t\tgoCount++\n\t\tsearch := search\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tr := search(ctx, filter)\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\tcase searchCh <- r:\n\t\t\t}\n\t\t}()\n\t}\n\te.Add(\"search.goroutines\", goCount)\n\n\t// Wait for our searches to complete then close our searchCh.\n\tgo func() { wg.Wait(); close(searchCh) }()\n\n\t// Collect all IDs from searches and count them. When one hits\n\t// the total number of filters send the matching pet to the caller.\n\tm := map[string]int{}\n\tmatchCh := make(chan string, 1)\n\tgo func() {\n\t\tdefer close(matchCh)\n\t\tfor ids := range searchCh {\n\t\t\tfor _, id := range ids {\n\t\t\t\tcount := m[id]\n\t\t\t\tcount++\n\t\t\t\tm[id] = count\n\t\t\t\tif count == filters {\n\t\t\t\t\tmatchCh <- id\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n\n\t// This handles all our matches getting returned.\n\tvalCount := 0\n\tlatency := 0\n\tdefer func() {\n\t\tif valCount > 0 {\n\t\t\te.Add(\"upstream.recv.latency.avg.ns\", latency/valCount)\n\t\t}\n\t}()\n\n\tfor {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\tcase id, ok := <-matchCh:\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tstart := time.Now()\n\t\t\tout <- storage.SearchItem{Pet: d.ids[id]}\n\t\t\tvalCount++\n\t\t\tlatency += int(time.Since(start))\n\t\t}\n\t}\n}\n\n// returnAll streams all the pets that we have.\nfunc (d *Data) returnAll(ctx context.Context, out chan storage.SearchItem) {\n\te := log.NewEvent(\"mem.data.returnAll()\")\n\tdefer e.Done(ctx)\n\n\tstart := time.Now()\n\tcount := 0\n\tdefer func() {\n\t\te.Add(\"latency.ns\", int(time.Since(start)))\n\t\te.Add(\"count\", count)\n\t}()\n\n\tfor _, p := range d.ids {\n\t\tcount++\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\tcase out <- storage.SearchItem{Pet: p}:\n\t\t}\n\t}\n}\n\n// byNames returns IDs of pets that have the names matched in the filter.\nfunc (d *Data) byNames(ctx context.Context, filter *pb.SearchPetsReq) []string {\n\tif len(filter.Names) == 0 {\n\t\treturn nil\n\t}\n\n\te := log.NewEvent(\"mem.data.byNames()\")\n\tdefer e.Done(ctx)\n\n\tstart := time.Now()\n\tcount := 0\n\tdefer func() {\n\t\te.Add(\"latency.ns\", int(time.Since(start)))\n\t\te.Add(\"count\", count)\n\t}()\n\tvar ids []string\n\tfor _, n := range filter.Names {\n\t\tcount++\n\t\tif ctx.Err() != nil {\n\t\t\treturn nil\n\t\t}\n\t\tp, ok := d.names[n]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tfor id := range p {\n\t\t\tids = append(ids, id)\n\t\t}\n\t}\n\treturn ids\n}\n\n// byTypes returns IDs of pets that have the types matched in the filter.\nfunc (d *Data) byTypes(ctx context.Context, filter *pb.SearchPetsReq) []string {\n\tif len(filter.Types) == 0 {\n\t\treturn nil\n\t}\n\te := log.NewEvent(\"mem.data.byTypes()\")\n\tdefer e.Done(ctx)\n\n\tstart := time.Now()\n\tcount := 0\n\tdefer func() {\n\t\te.Add(\"latency.ns\", int(time.Since(start)))\n\t\te.Add(\"count\", count)\n\t}()\n\tvar ids []string\n\tfor _, t := range filter.Types {\n\t\tcount++\n\t\tif ctx.Err() != nil {\n\t\t\treturn nil\n\t\t}\n\t\tp, ok := d.types[t]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tfor id := range p {\n\t\t\tids = append(ids, id)\n\t\t}\n\t}\n\treturn ids\n}\n\n// byBirthdays returns IDs of pets that have the birthdays matched in the filter.\nfunc (d *Data) byBirthdays(ctx context.Context, filter *pb.SearchPetsReq) []string {\n\tif filter.BirthdateRange == nil {\n\t\treturn nil\n\t}\n\n\te := log.NewEvent(\"mem.data.byBirthdays()\")\n\tdefer e.Done(ctx)\n\n\tstart := time.Now()\n\tcount := 0\n\tdefer func() {\n\t\te.Add(\"latency.ns\", int(time.Since(start)))\n\t\te.Add(\"count\", count)\n\t}()\n\n\tvar ids []string\n\td.birthday.DoRange(\n\t\tfunc(c llrb.Comparable) (done bool) {\n\t\t\tfor _, p := range c.(birthdays) {\n\t\t\t\tif ctx.Err() != nil {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tids = append(ids, p.Id)\n\t\t\t}\n\t\t\treturn\n\t\t},\n\t\tbirthdayGet{&pb.Pet{Birthday: filter.BirthdateRange.Start}},\n\t\tbirthdayGet{&pb.Pet{Birthday: filter.BirthdateRange.End}},\n\t)\n\tif ctx.Err() != nil {\n\t\treturn nil\n\t}\n\tcount = len(ids)\n\treturn ids\n}\n"
  },
  {
    "path": "chapter/13/petstore-provider/internal/client/internal/server/storage/mem/mem_test.go",
    "content": "package mem\n\nimport (\n\t\"context\"\n\t\"sort\"\n\t\"strconv\"\n\t\"testing\"\n\n\t\"github.com/kylelemons/godebug/pretty\"\n\tdpb \"google.golang.org/genproto/googleapis/type/date\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/13/petstore-provider/internal/client/internal/server/storage\"\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/13/petstore-provider/internal/client/proto\"\n)\n\n// This tests we implement the interface.\nvar _ storage.Data = &Data{}\n\nvar pets = []*pb.Pet{\n\t{\n\t\tId:       \"0\",\n\t\tName:     \"Adam\",\n\t\tType:     pb.PetType_PTCanine,\n\t\tBirthday: &dpb.Date{Month: 1, Day: 1, Year: 2020},\n\t},\n\t{\n\t\tId:       \"1\",\n\t\tName:     \"Becky\",\n\t\tType:     pb.PetType_PTFeline,\n\t\tBirthday: &dpb.Date{Month: 2, Day: 1, Year: 2020},\n\t},\n\t{\n\t\tId:       \"2\",\n\t\tName:     \"Calvin\",\n\t\tType:     pb.PetType_PTFeline,\n\t\tBirthday: &dpb.Date{Month: 2, Day: 2, Year: 2020},\n\t},\n\t{\n\t\tId:       \"3\",\n\t\tName:     \"David\",\n\t\tType:     pb.PetType_PTBird,\n\t\tBirthday: &dpb.Date{Month: 2, Day: 2, Year: 2021},\n\t},\n\t{\n\t\tId:       \"4\",\n\t\tName:     \"Elaine\",\n\t\tType:     pb.PetType_PTReptile,\n\t\tBirthday: &dpb.Date{Month: 2, Day: 2, Year: 2021},\n\t},\n\t{\n\t\tId:       \"5\",\n\t\tName:     \"Elaine\",\n\t\tType:     pb.PetType_PTReptile,\n\t\tBirthday: &dpb.Date{Month: 2, Day: 3, Year: 2021},\n\t},\n}\n\n// makePets takes the global \"pets\" var and clones everything in it and puts it into\n// a *Data so we have test data.\nfunc makePets() *Data {\n\td := New()\n\n\tn := []*pb.Pet{}\n\tfor _, p := range pets {\n\t\tn = append(n, proto.Clone(p).(*pb.Pet))\n\t}\n\n\td.AddPets(context.Background(), n)\n\treturn d\n}\n\nfunc TestByNames(t *testing.T) {\n\td := makePets()\n\n\tgot := d.byNames(context.Background(), &pb.SearchPetsReq{Names: []string{\"David\", \"Elaine\"}})\n\tsort.Strings(got)\n\n\twant := []string{\"3\", \"4\", \"5\"}\n\tif diff := pretty.Compare(want, got); diff != \"\" {\n\t\tt.Errorf(\"TestByNames: -want/+got:\\n%s\", diff)\n\t}\n}\n\nfunc TestByTypes(t *testing.T) {\n\td := makePets()\n\n\tgot := d.byTypes(context.Background(), &pb.SearchPetsReq{Types: []pb.PetType{pb.PetType_PTCanine, pb.PetType_PTReptile}})\n\tsort.Strings(got)\n\n\twant := []string{\"0\", \"4\", \"5\"}\n\tif diff := pretty.Compare(want, got); diff != \"\" {\n\t\tt.Errorf(\"TestByTypes: -want/+got:\\n%s\", diff)\n\t}\n}\n\nfunc TestByBirthdays(t *testing.T) {\n\td := makePets()\n\n\tgot := d.byBirthdays(\n\t\tcontext.Background(),\n\t\t&pb.SearchPetsReq{\n\t\t\tBirthdateRange: &pb.DateRange{\n\t\t\t\tStart: &dpb.Date{Month: 2, Day: 1, Year: 2020},\n\t\t\t\tEnd:   &dpb.Date{Month: 2, Day: 3, Year: 2021},\n\t\t\t},\n\t\t},\n\t)\n\tsort.Strings(got)\n\n\twant := []string{\"1\", \"2\", \"3\", \"4\"}\n\tif diff := pretty.Compare(want, got); diff != \"\" {\n\t\tt.Errorf(\"TestByBirthdays: -want/+got:\\n%s\", diff)\n\t}\n}\n\nfunc TestDeletePets(t *testing.T) {\n\td := makePets()\n\n\tdeletions := []string{\"3\", \"5\", \"20\"}\n\n\tif err := d.DeletePets(context.Background(), deletions); err != nil {\n\t\tt.Fatalf(\"TestDeletePets: got err == %v, want err == nil\", err)\n\t}\n\n\t// Don't check the last deletion, it is only there to make sure\n\t// a non-existent value doesn't do anything.\n\tfor _, id := range deletions[:len(deletions)-1] {\n\t\tif _, ok := d.ids[id]; ok {\n\t\t\tt.Errorf(\"TestDeletePets: found ids[%s]\", id)\n\t\t}\n\t\ti, _ := strconv.Atoi(id)\n\n\t\tif m, ok := d.names[pets[i].Name]; ok {\n\t\t\tif _, ok := m[id]; ok {\n\t\t\t\tt.Errorf(\"TestDeletePets: found(%s) in names\", id)\n\t\t\t}\n\t\t}\n\t\tif m, ok := d.types[pets[i].Type]; ok {\n\t\t\tif _, ok := m[id]; ok {\n\t\t\t\tt.Errorf(\"TestDeletePets: found(%s) in types\", id)\n\t\t\t}\n\t\t}\n\n\t\tv := d.birthday.Get(birthdayGet{pets[i]})\n\t\tif v != nil {\n\t\t\tif _, ok := v.(birthdays)[id]; ok {\n\t\t\t\tt.Errorf(\"TestDeletePets: found(%s) in birthday tree\", id)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestSearchPets(t *testing.T) {\n\td := makePets()\n\n\tch := d.SearchPets(\n\t\tcontext.Background(),\n\t\t&pb.SearchPetsReq{\n\t\t\tNames: []string{\n\t\t\t\t\"Becky\",\n\t\t\t\t\"Calvin\",\n\t\t\t\t\"David\",\n\t\t\t\t\"Elaine\",\n\t\t\t},\n\t\t\tTypes: []pb.PetType{\n\t\t\t\tpb.PetType_PTReptile,\n\t\t\t\tpb.PetType_PTFeline,\n\t\t\t},\n\t\t\tBirthdateRange: &pb.DateRange{\n\t\t\t\tStart: &dpb.Date{Month: 2, Day: 2, Year: 2021},\n\t\t\t\tEnd:   &dpb.Date{Month: 2, Day: 3, Year: 2021},\n\t\t\t},\n\t\t},\n\t)\n\n\tgot := []storage.SearchItem{}\n\tfor item := range ch {\n\t\tgot = append(got, item)\n\t}\n\n\twant := []storage.SearchItem{{Pet: pets[4]}}\n\n\tconfig := pretty.Config{TrackCycles: true}\n\tif diff := config.Compare(want, got); diff != \"\" {\n\t\tt.Errorf(\"TestSearchPets: -want/+got:\\n%s\", diff)\n\t}\n}\n"
  },
  {
    "path": "chapter/13/petstore-provider/internal/client/internal/server/storage/storage.go",
    "content": "package storage\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"time\"\n\n\tdpb \"google.golang.org/genproto/googleapis/type/date\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/13/petstore-provider/internal/client/internal/server/errors\"\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/13/petstore-provider/internal/client/proto\"\n)\n\n// Data represents our data storage.\ntype Data interface {\n\t// AddPets adds pet entries into storage.\n\tAddPets(ctx context.Context, pets []*pb.Pet) error\n\t// UpdatePets updates pet entries in storage.\n\tUpdatePets(ctx context.Context, pets []*pb.Pet) error\n\t// DeletePets deletes pets in storage by their ID. Will not error\n\t// on IDs not found.\n\tDeletePets(ctx context.Context, ids []string) error\n\t// SearchPets searches storage for pet entries that match the\n\t// filter.\n\tSearchPets(ctx context.Context, filter *pb.SearchPetsReq) chan SearchItem\n}\n\n// SearchItem is an item returned by a search.\ntype SearchItem struct {\n\t// Pet is the pet that matched the search filters.\n\tPet *pb.Pet\n\t// Error indicates that there was an error. If set the channel\n\t// will close after this entry.\n\tError error\n}\n\n// ValidatePet validates that *pb.Pet has valid fields.\nfunc ValidatePet(ctx context.Context, p *pb.Pet, forUpdate bool) error {\n\tif forUpdate && p.Id == \"\" {\n\t\treturn errors.New(ctx, \"updates must have the Id field set\")\n\t} else {\n\t\tif !forUpdate && p.Id != \"\" {\n\t\t\treturn errors.New(ctx, \"cannot set the Id field\")\n\t\t}\n\t}\n\tp.Name = strings.TrimSpace(p.Name)\n\tif p.Name == \"\" {\n\t\treturn errors.New(ctx, \"cannot have a pet without a name\")\n\t}\n\n\tif p.Type == pb.PetType_PTUnknown {\n\t\treturn errors.New(ctx, \"cannot have an unknown pet type\")\n\t}\n\n\t_, err := BirthdayToTime(ctx, p.Birthday)\n\tif err != nil {\n\t\treturn errors.Errorf(ctx, \"pet(%s) had an error in its birthday: %w\", p.Name, err)\n\t}\n\treturn nil\n\n}\n\n// BirthdayToTime converts the *pb.Pet.Birthday field to a time.Time object.\nfunc BirthdayToTime(ctx context.Context, d *dpb.Date) (time.Time, error) {\n\tif d.Month < 1 || d.Month > 12 {\n\t\treturn time.Time{}, errors.Errorf(ctx, \"month must be 1-12, was %d\", d.Month)\n\t}\n\tif d.Day < 1 || d.Day > 31 {\n\t\treturn time.Time{}, errors.Errorf(ctx, \"day(%d) was invalid\", d.Day)\n\t}\n\tt := time.Date(int(d.Year), time.Month(d.Month), int(d.Day), 0, 0, 0, 0, time.UTC)\n\n\tif t.Month() != time.Month(d.Month) {\n\t\treturn time.Time{}, errors.Errorf(ctx, \"month %v does not have %d days\", time.Month(d.Month), d.Day)\n\t}\n\treturn t, nil\n}\n"
  },
  {
    "path": "chapter/13/petstore-provider/internal/client/internal/server/telemetry/metrics/metrics.go",
    "content": "/*\nPackage metrics provides setup of metrics that can be used internally to measure various application states.\nAll metrics for the application are defined here and other applications use this package to grab the metrics\nand use them. This package will also report any metric that is not used in the first 10 seconds after the app has started\nto prevent useless metrics from existing, as all metrics should be grabbed by that time.\n\nIn a package you want to set metrics, you can do it as follows:\n\tvar addCount metrics.Int64Counter\n\n\tfunc init() {\n\t\taddCounter = metrics.Get.Int64(\"petstore/server/AddPets/requests\")\n\t}\n\t...\n\n\tfunc (s *Server) AddPets(ctx context.Context, req *pb.AddPetsReq) (*pb.AddpetsResp, error) {\n\t\t...\n\t\t// Do this if you have multiple changes that don't require special labels per update.\n\t\tmetrics.Meter.RecordBatch(ctx, nil, addCounter.Measure(ctx, 1))\n\t\t// Do this if you only need to make one change or need special labels.\n\t\taddCounter.Add(ctx, 1, attribute.String(\"label\", \"value\")\n\t\t...\n\t}\n\n\nTo cause metrics to be exported package main():\n\tfunc main() {\n\t\t...\n\t\tstop, err := metrics.Start(ctx, metrics.OTELGRPC{Addr: \"ip:port\"})\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\tdefer stop()\n\t\t...\n\t}\n*/\npackage metrics\n\nimport (\n\t\"html/template\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/13/petstore-provider/internal/client/internal/server/log\"\n\n\t\"go.opentelemetry.io/otel/metric\"\n\t\"go.opentelemetry.io/otel/metric/global\"\n)\n\ntype metricType int\n\nconst (\n\tunknown     = 0\n\tmtInt64     = 1\n\tmtInt64Hist = 2\n\tmtInt64UD   = 3\n)\n\ntype metricDef struct {\n\tmtype metricType\n\tname  string\n\tdesc  string\n}\n\nvar metrics = []metricDef{\n\t// Histograms\n\t{mtInt64Hist, \"petstore/server/AddPets/latency\", \"The latency of an AddPets() request in nanoseconds\"},\n\t{mtInt64Hist, \"petstore/server/DeletePets/latency\", \"The latency of an DeletePets() request in nanoseconds\"},\n\t{mtInt64Hist, \"petstore/server/UpdatePets/latency\", \"The latency of an UpdatePets() request in nanoseconds\"},\n\t{mtInt64Hist, \"petstore/server/SearchPets/latency\", \"The latency of a SearchPets() request in nanoseconds\"},\n\t// Counters\n\t{mtInt64, \"petstore/server/AddPets/requests\", \"The total requests made to AddPets()\"},\n\t{mtInt64, \"petstore/server/DeletePets/requests\", \"The total requests made to DeletePets()\"},\n\t{mtInt64, \"petstore/server/UpdatePets/requests\", \"The total requests made to UpdatePets()\"},\n\t{mtInt64, \"petstore/server/SearchPets/requests\", \"The total requests made to SearchPets()\"},\n\t{mtInt64, \"petstore/server/totals/requests\", \"The total requests made to the server\"},\n\n\t{mtInt64, \"petstore/server/AddPets/errors\", \"The total error count\"},\n\t{mtInt64, \"petstore/server/DeletePets/errors\", \"The total error couunt\"},\n\t{mtInt64, \"petstore/server/UpdatePets/errors\", \"The total error count\"},\n\t{mtInt64, \"petstore/server/SearchPets/errors\", \"The total error count\"},\n\t{mtInt64, \"petstore/server/totals/errors\", \"The total error count for all RPCs\"},\n\n\t// UpDown Counters\n\t{mtInt64UD, \"petstore/server/AddPets/current\", \"The amount of requests currently being proccessed\"},\n\t{mtInt64UD, \"petstore/server/DeletePets/current\", \"The amount of requests currently being proccessed\"},\n\t{mtInt64UD, \"petstore/server/UpdatePets/current\", \"The amount of requests currently being proccessed\"},\n\t{mtInt64UD, \"petstore/server/SearchPets/current\", \"The amount of requests currently being proccessed\"},\n}\n\n// Meter is the meter for the petstore.\nvar Meter = global.Meter(\"petstore\")\n\n// Get is used to lookup metrics by name.\nvar Get = newLookups()\n\nvar unusedMetricsTmpl = template.Must(\n\ttemplate.New(\"\").Parse(\n\t\t`\nThe following metrics appeart to be unused:\n{{- range .}}\n\t{{.}}\n{{- end }}\n`,\n\t),\n)\n\n// Lookups provides lookups for metrics based on their names.\ntype Lookups struct {\n\tmtInt64Hist map[string]metric.Int64Histogram\n\tmtInt64UD   map[string]metric.Int64UpDownCounter\n\tmtInt64     map[string]metric.Int64Counter\n\n\tmu   sync.Mutex\n\tused map[string]bool\n}\n\nfunc newLookups() *Lookups {\n\tl := &Lookups{\n\t\tmtInt64Hist: map[string]metric.Int64Histogram{},\n\t\tmtInt64:     map[string]metric.Int64Counter{},\n\t\tmtInt64UD:   map[string]metric.Int64UpDownCounter{},\n\t\tused:        map[string]bool{},\n\t}\n\n\texists := map[string]bool{}\n\tfor _, m := range metrics {\n\t\tif m.mtype == unknown {\n\t\t\tlog.Logger.Fatalf(\"metric with type(%v) cannot be added\", m.mtype)\n\t\t}\n\t\tif m.name == \"\" {\n\t\t\tlog.Logger.Fatalf(\"metric cannot be missing a name\")\n\t\t}\n\t\tif m.desc == \"\" {\n\t\t\tlog.Logger.Fatalf(\"metric cannot be missing a desc\")\n\t\t}\n\t\tif exists[m.name] {\n\t\t\tlog.Logger.Fatalf(\"cannot have two metrics with same name(%s)\", m.name)\n\t\t}\n\t\texists[m.name] = true\n\n\t\tswitch m.mtype {\n\t\tcase mtInt64Hist:\n\t\t\tl.mtInt64Hist[m.name] = metric.Must(Meter).NewInt64Histogram(m.name, metric.WithDescription(m.desc))\n\t\tcase mtInt64UD:\n\t\t\tl.mtInt64UD[m.name] = metric.Must(Meter).NewInt64UpDownCounter(m.name, metric.WithDescription(m.desc))\n\t\tcase mtInt64:\n\t\t\tl.mtInt64[m.name] = metric.Must(Meter).NewInt64Counter(m.name, metric.WithDescription(m.desc))\n\t\tdefault:\n\t\t\tlog.Logger.Fatalf(\"bug: we defined a metric type(%v) without adding support\", m.mtype)\n\t\t}\n\t}\n\tgo func() {\n\t\ttime.Sleep(10 * time.Second)\n\t\tunused := l.unused()\n\t\ts := strings.Builder{}\n\t\tif err := unusedMetricsTmpl.Execute(&s, unused); err != nil {\n\t\t\tlog.Logger.Fatalf(\"unusedMetricTmpl execute error: %s\", err)\n\t\t}\n\t\tlog.Logger.Println(s.String())\n\t}()\n\treturn l\n}\n\n// Int64 grabs the Int64Counter metric named \"s\". If not found, panics.\nfunc (l *Lookups) Int64(s string) metric.Int64Counter {\n\tl.mu.Lock()\n\tdefer l.mu.Unlock()\n\n\tm, ok := l.mtInt64[s]\n\tif !ok {\n\t\tlog.Logger.Fatalf(\"int64 metric(%s) is not defined\", s)\n\t}\n\tl.used[s] = true\n\treturn m\n}\n\n// Int64s grabs a list of Int64Counters.\nfunc (l *Lookups) Int64s(s ...string) []metric.Int64Counter {\n\tv := make([]metric.Int64Counter, 0, len(s))\n\tfor _, name := range s {\n\t\tv = append(v, l.Int64(name))\n\t}\n\treturn v\n}\n\n// Int64UD grabs the Int64UpDownCounter metric named \"s\". If not found, panics.\nfunc (l *Lookups) Int64UD(s string) metric.Int64UpDownCounter {\n\tl.mu.Lock()\n\tdefer l.mu.Unlock()\n\n\tm, ok := l.mtInt64UD[s]\n\tif !ok {\n\t\tlog.Logger.Fatalf(\"int64ud metric(%s) is not defined\", s)\n\t}\n\tl.used[s] = true\n\treturn m\n}\n\n// Int64UDs grabs a list of Int64UpDownCounters.\nfunc (l *Lookups) Int64UDs(s ...string) []metric.Int64UpDownCounter {\n\tv := make([]metric.Int64UpDownCounter, 0, len(s))\n\tfor _, name := range s {\n\t\tv = append(v, l.Int64UD(name))\n\t}\n\treturn v\n}\n\n// Int64Hist grabs the Int64Histogram metric named \"s\". If not found, panics.\nfunc (l *Lookups) Int64Hist(s string) metric.Int64Histogram {\n\tl.mu.Lock()\n\tdefer l.mu.Unlock()\n\n\tm, ok := l.mtInt64Hist[s]\n\tif !ok {\n\t\tlog.Logger.Fatalf(\"int64 histogram metric(%s) is not defined\", s)\n\t}\n\tl.used[s] = true\n\treturn m\n}\n\nfunc (l *Lookups) Int64Hists(s ...string) []metric.Int64Histogram {\n\tv := make([]metric.Int64Histogram, 0, len(s))\n\tfor _, name := range s {\n\t\tv = append(v, l.Int64Hist(name))\n\t}\n\treturn v\n}\n\nfunc (l *Lookups) unused() []string {\n\tl.mu.Lock()\n\tdefer l.mu.Unlock()\n\n\tunused := []string{}\n\tfor k := range l.mtInt64Hist {\n\t\tif !l.used[k] {\n\t\t\tunused = append(unused, k)\n\t\t}\n\t}\n\tfor k := range l.mtInt64 {\n\t\tif !l.used[k] {\n\t\t\tunused = append(unused, k)\n\t\t}\n\t}\n\tsort.Strings(unused)\n\treturn unused\n}\n"
  },
  {
    "path": "chapter/13/petstore-provider/internal/client/internal/server/telemetry/metrics/start.go",
    "content": "package metrics\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlpmetric\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc\"\n\t\"go.opentelemetry.io/otel/metric/global\"\n\t\"go.opentelemetry.io/otel/sdk/metric/controller/basic\"\n\tprocessor \"go.opentelemetry.io/otel/sdk/metric/processor/basic\"\n\t\"go.opentelemetry.io/otel/sdk/metric/selector/simple\"\n)\n\n// Controller represents the controller to send metrics to.\ntype Controller interface {\n\tisController()\n}\n\n// OTELGRPC represents exporting to the \"go.opentelemetry.io/otel/sdk/metric/controller/basic\" controller.\ntype OTELGRPC struct {\n\t// Addr is the local address to export on.\n\tAddr string\n}\n\nfunc (o OTELGRPC) isController() {}\n\n// Stop is used to stop OTEL metric handling.\ntype Stop func()\n\n// Start is used to start OTEL metric handling.\nfunc Start(ctx context.Context, c Controller) (Stop, error) {\n\tcontrol, err := newController(ctx, c)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\terr = control.Start(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn func() {\n\t\tctx, cancel := context.WithTimeout(ctx, 1*time.Second)\n\t\tdefer cancel()\n\t\tif err := control.Stop(ctx); err != nil {\n\t\t\totel.Handle(err)\n\t\t}\n\t}, nil\n}\n\nfunc newController(ctx context.Context, c Controller) (*basic.Controller, error) {\n\tswitch v := c.(type) {\n\tcase OTELGRPC:\n\t\treturn otelGRPC(ctx, v)\n\t}\n\treturn nil, fmt.Errorf(\"%T is not a valid Controller\", c)\n}\n\nfunc otelGRPC(ctx context.Context, args OTELGRPC) (*basic.Controller, error) {\n\tmetricClient := otlpmetricgrpc.NewClient(\n\t\totlpmetricgrpc.WithInsecure(),\n\t\totlpmetricgrpc.WithEndpoint(args.Addr),\n\t)\n\tmetricExp, err := otlpmetric.New(ctx, metricClient)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"Failed to create the collector metric exporter\")\n\t}\n\n\tpusher := basic.New(\n\t\tprocessor.NewFactory(\n\t\t\tsimple.NewWithHistogramDistribution(),\n\t\t\tmetricExp,\n\t\t),\n\t\tbasic.WithExporter(metricExp),\n\t\tbasic.WithCollectPeriod(10*time.Second),\n\t)\n\tglobal.SetMeterProvider(pusher)\n\treturn pusher, nil\n}\n"
  },
  {
    "path": "chapter/13/petstore-provider/internal/client/internal/server/telemetry/tracing/sampler/sampler.go",
    "content": "/*\nPackage sampler offers a Sampler that looks for a TraceID.Valid() == true or a gRPC metadata key called \"trace\"\nand if they exist will sample. Otherwise it looks to a child Sampler to determine based upon whatever sampler\nalgorithm is used.\n\nIn addition we offer the ability to switch out the underlying sampler at anytime in a thread-safe way.\n\nYou can construct a new Sampler like so:\n\ts, err := New(trace.NeverSample)\n\tif err != nil {\n\t\t// Do something\n\t}\n\nThe above Sampler would only trace if a TraceID.Valid() == true or gRCP metadate key called \"trace\" existed.\n\nIf we want to trace 1% of the time as well, we can do the following:\n\ts, err := New(trace.TraceIDRatioBased(.01))\n\tif err != nil {\n\t\t// Do something\n\t}\n*/\npackage sampler\n\nimport (\n\t\"fmt\"\n\t\"sync/atomic\"\n\n\t\"go.opentelemetry.io/otel/sdk/trace\"\n\totelTrace \"go.opentelemetry.io/otel/trace\"\n\t\"google.golang.org/grpc/metadata\"\n)\n\nconst desc = `This sampler samples if TracdID.Valid(), gRPC metadata contains key \"trace\" or the child sampler decides to sample`\n\n// Sampler decides whether a trace should be sampled and exported. This sampler will sample if\n// paramters TraceID.Valid() == true or the Context contains gRPC metadata that has key \"trace\" (it doesn't care about values).\ntype Sampler struct {\n\t// child stores a *trace.Sampler. trace.Sampler is an interface. Because atomic.Value cares about\n\t// the underlying type, you can't just store trace.Sampler. So we do a pointer, which is the only valid\n\t// use of *interface I've ever seen.\n\tchild atomic.Value // *trace.Sampler\n}\n\n// New creates a new Sampler with the child Sampler used if TraceID.Valid() == false and gRPC metadata does not contain\n// key \"trace\".\nfunc New(child trace.Sampler) (*Sampler, error) {\n\tif child == nil {\n\t\treturn nil, fmt.Errorf(\"child cannot == nil\")\n\t}\n\n\ts := &Sampler{}\n\ts.child.Store(&child)\n\treturn s, nil\n}\n\n// ShouldSample implements trace.Sampler.ShouldSample.\nfunc (s *Sampler) ShouldSample(p trace.SamplingParameters) trace.SamplingResult {\n\tpsc := otelTrace.SpanContextFromContext(p.ParentContext)\n\tif psc.IsValid() {\n\t\tif psc.IsRemote() {\n\t\t\tif psc.IsSampled() {\n\t\t\t\treturn trace.SamplingResult{\n\t\t\t\t\tDecision:   trace.RecordAndSample,\n\t\t\t\t\tTracestate: psc.TraceState(),\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif psc.IsSampled() {\n\t\t\treturn trace.SamplingResult{\n\t\t\t\tDecision:   trace.RecordAndSample,\n\t\t\t\tTracestate: psc.TraceState(),\n\t\t\t}\n\t\t}\n\t}\n\tmd, ok := metadata.FromIncomingContext(p.ParentContext)\n\tif !ok {\n\t\treturn (*s.child.Load().(*trace.Sampler)).ShouldSample(p)\n\t}\n\n\tif _, ok := md[\"trace\"]; ok {\n\t\tpsc := otelTrace.SpanContextFromContext(p.ParentContext)\n\t\treturn trace.SamplingResult{\n\t\t\tDecision:   trace.RecordAndSample,\n\t\t\tTracestate: psc.TraceState(),\n\t\t}\n\t}\n\n\treturn (*s.child.Load().(*trace.Sampler)).ShouldSample(p)\n}\n\n// Description implements trace.Sampler.Description().\nfunc (s *Sampler) Description() string {\n\treturn desc\n}\n\n// Switch switches the underlying trace.Sampler.\nfunc (s *Sampler) Switch(sampler trace.Sampler) {\n\tif sampler == nil {\n\t\tpanic(\"cannot call Switch() with a nil Sampler\")\n\t}\n\ts.child.Store(&sampler)\n}\n"
  },
  {
    "path": "chapter/13/petstore-provider/internal/client/internal/server/telemetry/tracing/tracing.go",
    "content": "/*\nPackage tracing provides functions for starting and stopping our Open Telemetry tracing.\nThis package is intended to be used from main and is simple to use. We offer a few\nchoices on where traces export to. Here is an example to trace to stderr for all requests:\n\tfunc main() {\n\t\tctx := context.Background()\n\t\t// Set us up to always sample. The \"trace\" package is: \"petstore/server/SearchPets/latency\"\n\t\ttracing.Sampler.Switch(trace.AlwaysSample())\n\t\t// Start our tracing and pass the empty Stderr tracing arguments.\n\t\t// Stderr{} has no required fields.\n\t\tstop, err := tracing.Start(ctx, tracing.Stderr{})\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"problem starting telemetry: %s\", err)\n\t\t}\n\n\t\t// Stop kills our exporter when main() ends.\n\t\tdefer stop()\n\t}\n*/\npackage tracing\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc\"\n\t\"go.opentelemetry.io/otel/exporters/stdout/stdouttrace\"\n\t\"go.opentelemetry.io/otel/propagation\"\n\t\"go.opentelemetry.io/otel/sdk/resource\"\n\tsdktrace \"go.opentelemetry.io/otel/sdk/trace\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.4.0\"\n\t\"go.opentelemetry.io/otel/trace\"\n\t\"google.golang.org/grpc\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/13/petstore-provider/internal/client/internal/server/telemetry/tracing/sampler\"\n)\n\n// Tracer is the tracer initialized by Start().\nvar (\n\t// Tracer is the tracer initialized by Start().\n\tTracer trace.Tracer // *sdktrace.TracerProvider //otlptrace.Exporter\n\t// Sampler is our *sampler.Sampler used by the Tracer.\n\tSampler *sampler.Sampler\n)\n\nfunc init() {\n\ts, err := sampler.New(sdktrace.TraceIDRatioBased(1))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tSampler = s\n}\n\n// Exporter represents the exporter to send telemetry to.\ntype Exporter interface {\n\tisExporter()\n}\n\n// OTELGRPC represents exporting to the go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc exporter.\ntype OTELGRPC struct {\n\t// Addr is the local address to export on.\n\tAddr string\n}\n\nfunc (o OTELGRPC) isExporter() {}\n\n// Stderr exports trace data to os.Stderr.\ntype Stderr struct{}\n\nfunc (s Stderr) isExporter() {}\n\n// File exports trace data to a file. If the file exists, it is overwritten.\ntype File struct {\n\t// Path is the path to the file.\n\tPath string\n}\n\nfunc (f File) isExporter() {}\n\n// Stop stops our Open Telemetry exporter.\ntype Stop func()\n\n// Start creates the OTEL exporter and configures the trace providers.\n// It returns a Stop() which will stop the exporter.\nfunc Start(ctx context.Context, e Exporter) (Stop, error) {\n\tlog.Println(\"Sampler: \", Sampler)\n\ttp, err := newTraceExporter(ctx, e)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tTracer = tp.Tracer(\"petstore\")\n\n\treturn func() {\n\t\tvar cancel context.CancelFunc\n\t\tctx, cancel = context.WithTimeout(ctx, 1*time.Second)\n\t\tdefer cancel()\n\n\t\tif err := tp.Shutdown(ctx); err != nil {\n\t\t\totel.Handle(err)\n\t\t}\n\t}, nil\n}\n\n// newTracerExporter creates an OTLP exporter with our tracer information.\nfunc newTraceExporter(ctx context.Context, e Exporter) (*sdktrace.TracerProvider, error) {\n\tvar exp sdktrace.SpanExporter\n\tvar err error\n\tswitch v := e.(type) {\n\tcase OTELGRPC:\n\t\texp, err = otelGRPC(ctx, v)\n\tcase Stderr:\n\t\texp, err = newFileExporter(os.Stderr)\n\tcase File:\n\t\tf, err := os.Create(v.Path)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\texp, err = newFileExporter(f)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"%T is not a valid Exporter\", e)\n\t}\n\n\tres, err := resource.New(\n\t\tctx,\n\t\tresource.WithFromEnv(),\n\t\tresource.WithProcess(),\n\t\tresource.WithTelemetrySDK(),\n\t\tresource.WithHost(),\n\t\tresource.WithAttributes(\n\t\t\t// the service name used to display traces in backends\n\t\t\tsemconv.ServiceNameKey.String(\"petstore\"),\n\t\t),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// set global propagator to tracecontext (the default is no-op).\n\totel.SetTextMapPropagator(propagation.TraceContext{})\n\n\tprov := sdktrace.NewTracerProvider(\n\t\tsdktrace.WithSampler(Sampler),\n\t\tsdktrace.WithBatcher(exp),\n\t\tsdktrace.WithResource(res),\n\t)\n\totel.SetTracerProvider(prov)\n\treturn prov, nil\n}\n\n// newFileExporter creates an exporter that writes to a file.\nfunc newFileExporter(w io.Writer) (sdktrace.SpanExporter, error) {\n\treturn stdouttrace.New(\n\t\tstdouttrace.WithWriter(w),\n\t\tstdouttrace.WithPrettyPrint(),\n\t)\n}\n\nfunc otelGRPC(ctx context.Context, e OTELGRPC) (sdktrace.SpanExporter, error) { //(*otlptrace.Exporter, error) {\n\texp, err := otlptrace.New(\n\t\tctx,\n\t\totlptracegrpc.NewClient(\n\t\t\totlptracegrpc.WithInsecure(),\n\t\t\totlptracegrpc.WithEndpoint(e.Addr),\n\t\t\totlptracegrpc.WithDialOption(grpc.WithBlock()),\n\t\t),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn exp, nil\n}\n"
  },
  {
    "path": "chapter/13/petstore-provider/internal/client/proto/buf.gen.yaml",
    "content": "version: v1\nplugins:\n  - name: go\n    out: ./\n    opt:\n      - paths=source_relative\n  - name: go-grpc\n    out: ./\n    opt:\n      - paths=source_relative\n"
  },
  {
    "path": "chapter/13/petstore-provider/internal/client/proto/buf.yaml",
    "content": "version: v1\ndeps:\n  - buf.build/googleapis/googleapis\nlint:\n  use:\n    - DEFAULT\nbreaking:\n  use:\n    - FILE\n"
  },
  {
    "path": "chapter/13/petstore-provider/internal/client/proto/petstore.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.25.0-devel\n// \tprotoc        v3.18.0\n// source: petstore.proto\n\npackage proto\n\nimport (\n\tdate \"google.golang.org/genproto/googleapis/type/date\"\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\n// Desribes the type of pets.\ntype PetType int32\n\nconst (\n\t// The type was not set.\n\tPetType_PTUnknown PetType = 0\n\t// The pet is a canine.\n\tPetType_PTCanine PetType = 1\n\t// The pet is a feline.\n\tPetType_PTFeline PetType = 2\n\t// The pet is a bird.\n\tPetType_PTBird PetType = 3\n\t// The pet is a reptile.\n\tPetType_PTReptile PetType = 4\n)\n\n// Enum value maps for PetType.\nvar (\n\tPetType_name = map[int32]string{\n\t\t0: \"PTUnknown\",\n\t\t1: \"PTCanine\",\n\t\t2: \"PTFeline\",\n\t\t3: \"PTBird\",\n\t\t4: \"PTReptile\",\n\t}\n\tPetType_value = map[string]int32{\n\t\t\"PTUnknown\": 0,\n\t\t\"PTCanine\":  1,\n\t\t\"PTFeline\":  2,\n\t\t\"PTBird\":    3,\n\t\t\"PTReptile\": 4,\n\t}\n)\n\nfunc (x PetType) Enum() *PetType {\n\tp := new(PetType)\n\t*p = x\n\treturn p\n}\n\nfunc (x PetType) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (PetType) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_petstore_proto_enumTypes[0].Descriptor()\n}\n\nfunc (PetType) Type() protoreflect.EnumType {\n\treturn &file_petstore_proto_enumTypes[0]\n}\n\nfunc (x PetType) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use PetType.Descriptor instead.\nfunc (PetType) EnumDescriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{0}\n}\n\n// Types of OTEL sampling we support.\ntype SamplerType int32\n\nconst (\n\tSamplerType_STUnknown SamplerType = 0\n\tSamplerType_STNever   SamplerType = 1\n\tSamplerType_STAlways  SamplerType = 2\n\tSamplerType_STFloat   SamplerType = 3\n)\n\n// Enum value maps for SamplerType.\nvar (\n\tSamplerType_name = map[int32]string{\n\t\t0: \"STUnknown\",\n\t\t1: \"STNever\",\n\t\t2: \"STAlways\",\n\t\t3: \"STFloat\",\n\t}\n\tSamplerType_value = map[string]int32{\n\t\t\"STUnknown\": 0,\n\t\t\"STNever\":   1,\n\t\t\"STAlways\":  2,\n\t\t\"STFloat\":   3,\n\t}\n)\n\nfunc (x SamplerType) Enum() *SamplerType {\n\tp := new(SamplerType)\n\t*p = x\n\treturn p\n}\n\nfunc (x SamplerType) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (SamplerType) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_petstore_proto_enumTypes[1].Descriptor()\n}\n\nfunc (SamplerType) Type() protoreflect.EnumType {\n\treturn &file_petstore_proto_enumTypes[1]\n}\n\nfunc (x SamplerType) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use SamplerType.Descriptor instead.\nfunc (SamplerType) EnumDescriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{1}\n}\n\n// Represents a range of dates.\ntype DateRange struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// When to start the range, this is inclusive.\n\tStart *date.Date `protobuf:\"bytes,1,opt,name=start,proto3\" json:\"start,omitempty\"`\n\t// When to end the range, this is exclusive.\n\tEnd *date.Date `protobuf:\"bytes,2,opt,name=end,proto3\" json:\"end,omitempty\"`\n}\n\nfunc (x *DateRange) Reset() {\n\t*x = DateRange{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DateRange) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DateRange) ProtoMessage() {}\n\nfunc (x *DateRange) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_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 DateRange.ProtoReflect.Descriptor instead.\nfunc (*DateRange) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *DateRange) GetStart() *date.Date {\n\tif x != nil {\n\t\treturn x.Start\n\t}\n\treturn nil\n}\n\nfunc (x *DateRange) GetEnd() *date.Date {\n\tif x != nil {\n\t\treturn x.End\n\t}\n\treturn nil\n}\n\n// Represents a unique pet.\ntype Pet struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// A UUIDv4 for this pet. This can never be set on an AddPet().\n\tId string `protobuf:\"bytes,1,opt,name=id,proto3\" json:\"id,omitempty\"`\n\t// The name of the pet.\n\tName string `protobuf:\"bytes,2,opt,name=name,proto3\" json:\"name,omitempty\"`\n\t// The type of pet.\n\tType PetType `protobuf:\"varint,3,opt,name=type,proto3,enum=petstore.PetType\" json:\"type,omitempty\"`\n\t// The pet's birthday.\n\tBirthday *date.Date `protobuf:\"bytes,4,opt,name=birthday,proto3\" json:\"birthday,omitempty\"`\n}\n\nfunc (x *Pet) Reset() {\n\t*x = Pet{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Pet) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Pet) ProtoMessage() {}\n\nfunc (x *Pet) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_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 Pet.ProtoReflect.Descriptor instead.\nfunc (*Pet) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *Pet) GetId() string {\n\tif x != nil {\n\t\treturn x.Id\n\t}\n\treturn \"\"\n}\n\nfunc (x *Pet) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\nfunc (x *Pet) GetType() PetType {\n\tif x != nil {\n\t\treturn x.Type\n\t}\n\treturn PetType_PTUnknown\n}\n\nfunc (x *Pet) GetBirthday() *date.Date {\n\tif x != nil {\n\t\treturn x.Birthday\n\t}\n\treturn nil\n}\n\n// The request used to add a pets to the system.\ntype AddPetsReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The pet information to add. Pet.id must not be set.\n\tPets []*Pet `protobuf:\"bytes,1,rep,name=pets,proto3\" json:\"pets,omitempty\"`\n}\n\nfunc (x *AddPetsReq) Reset() {\n\t*x = AddPetsReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AddPetsReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AddPetsReq) ProtoMessage() {}\n\nfunc (x *AddPetsReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[2]\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 AddPetsReq.ProtoReflect.Descriptor instead.\nfunc (*AddPetsReq) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *AddPetsReq) GetPets() []*Pet {\n\tif x != nil {\n\t\treturn x.Pets\n\t}\n\treturn nil\n}\n\n// The response do AddPets().\ntype AddPetsResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The IDs of the pets that were added.\n\tIds []string `protobuf:\"bytes,1,rep,name=ids,proto3\" json:\"ids,omitempty\"`\n}\n\nfunc (x *AddPetsResp) Reset() {\n\t*x = AddPetsResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AddPetsResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AddPetsResp) ProtoMessage() {}\n\nfunc (x *AddPetsResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[3]\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 AddPetsResp.ProtoReflect.Descriptor instead.\nfunc (*AddPetsResp) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{3}\n}\n\nfunc (x *AddPetsResp) GetIds() []string {\n\tif x != nil {\n\t\treturn x.Ids\n\t}\n\treturn nil\n}\n\n// The request used to update pets in the system.\ntype UpdatePetsReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The pet information to update. Pet.id must be set.\n\tPets []*Pet `protobuf:\"bytes,1,rep,name=pets,proto3\" json:\"pets,omitempty\"`\n}\n\nfunc (x *UpdatePetsReq) Reset() {\n\t*x = UpdatePetsReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[4]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *UpdatePetsReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*UpdatePetsReq) ProtoMessage() {}\n\nfunc (x *UpdatePetsReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[4]\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 UpdatePetsReq.ProtoReflect.Descriptor instead.\nfunc (*UpdatePetsReq) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (x *UpdatePetsReq) GetPets() []*Pet {\n\tif x != nil {\n\t\treturn x.Pets\n\t}\n\treturn nil\n}\n\n// The response do UpdatePets().\ntype UpdatePetsResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *UpdatePetsResp) Reset() {\n\t*x = UpdatePetsResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[5]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *UpdatePetsResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*UpdatePetsResp) ProtoMessage() {}\n\nfunc (x *UpdatePetsResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[5]\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 UpdatePetsResp.ProtoReflect.Descriptor instead.\nfunc (*UpdatePetsResp) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{5}\n}\n\n// Used to indicate which pets to delete. This is an all or nothing request.\ntype DeletePetsReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The IDs of the pets to delete.\n\tIds []string `protobuf:\"bytes,1,rep,name=ids,proto3\" json:\"ids,omitempty\"`\n}\n\nfunc (x *DeletePetsReq) Reset() {\n\t*x = DeletePetsReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[6]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DeletePetsReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DeletePetsReq) ProtoMessage() {}\n\nfunc (x *DeletePetsReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[6]\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 DeletePetsReq.ProtoReflect.Descriptor instead.\nfunc (*DeletePetsReq) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{6}\n}\n\nfunc (x *DeletePetsReq) GetIds() []string {\n\tif x != nil {\n\t\treturn x.Ids\n\t}\n\treturn nil\n}\n\n// The response to a DeletePet().\ntype DeletePetsResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *DeletePetsResp) Reset() {\n\t*x = DeletePetsResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[7]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DeletePetsResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DeletePetsResp) ProtoMessage() {}\n\nfunc (x *DeletePetsResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[7]\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 DeletePetsResp.ProtoReflect.Descriptor instead.\nfunc (*DeletePetsResp) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{7}\n}\n\n// The request to search for pets.\ntype SearchPetsReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Pet names to filter by.\n\tNames []string `protobuf:\"bytes,1,rep,name=names,proto3\" json:\"names,omitempty\"`\n\t// Pet types to filter by.\n\tTypes []PetType `protobuf:\"varint,2,rep,packed,name=types,proto3,enum=petstore.PetType\" json:\"types,omitempty\"`\n\t// Birthdays to filter by.\n\tBirthdateRange *DateRange `protobuf:\"bytes,3,opt,name=birthdate_range,json=birthdateRange,proto3\" json:\"birthdate_range,omitempty\"`\n}\n\nfunc (x *SearchPetsReq) Reset() {\n\t*x = SearchPetsReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[8]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *SearchPetsReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*SearchPetsReq) ProtoMessage() {}\n\nfunc (x *SearchPetsReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[8]\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 SearchPetsReq.ProtoReflect.Descriptor instead.\nfunc (*SearchPetsReq) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{8}\n}\n\nfunc (x *SearchPetsReq) GetNames() []string {\n\tif x != nil {\n\t\treturn x.Names\n\t}\n\treturn nil\n}\n\nfunc (x *SearchPetsReq) GetTypes() []PetType {\n\tif x != nil {\n\t\treturn x.Types\n\t}\n\treturn nil\n}\n\nfunc (x *SearchPetsReq) GetBirthdateRange() *DateRange {\n\tif x != nil {\n\t\treturn x.BirthdateRange\n\t}\n\treturn nil\n}\n\ntype Sampler struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The type of sampling to change to.\n\tType SamplerType `protobuf:\"varint,1,opt,name=type,proto3,enum=petstore.SamplerType\" json:\"type,omitempty\"`\n\t// This is the sampling rate if type == STFloat. Values must be\n\t// > 0 and <= 1.0 .\n\tFloatValue float64 `protobuf:\"fixed64,2,opt,name=float_value,json=floatValue,proto3\" json:\"float_value,omitempty\"`\n}\n\nfunc (x *Sampler) Reset() {\n\t*x = Sampler{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[9]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Sampler) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Sampler) ProtoMessage() {}\n\nfunc (x *Sampler) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[9]\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 Sampler.ProtoReflect.Descriptor instead.\nfunc (*Sampler) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{9}\n}\n\nfunc (x *Sampler) GetType() SamplerType {\n\tif x != nil {\n\t\treturn x.Type\n\t}\n\treturn SamplerType_STUnknown\n}\n\nfunc (x *Sampler) GetFloatValue() float64 {\n\tif x != nil {\n\t\treturn x.FloatValue\n\t}\n\treturn 0\n}\n\n// Used to request we change the OTEL sampling.\ntype ChangeSamplerReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tSampler *Sampler `protobuf:\"bytes,1,opt,name=sampler,proto3\" json:\"sampler,omitempty\"`\n}\n\nfunc (x *ChangeSamplerReq) Reset() {\n\t*x = ChangeSamplerReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[10]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ChangeSamplerReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ChangeSamplerReq) ProtoMessage() {}\n\nfunc (x *ChangeSamplerReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[10]\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 ChangeSamplerReq.ProtoReflect.Descriptor instead.\nfunc (*ChangeSamplerReq) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{10}\n}\n\nfunc (x *ChangeSamplerReq) GetSampler() *Sampler {\n\tif x != nil {\n\t\treturn x.Sampler\n\t}\n\treturn nil\n}\n\n// The response to a sampling change.\ntype ChangeSamplerResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ChangeSamplerResp) Reset() {\n\t*x = ChangeSamplerResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[11]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ChangeSamplerResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ChangeSamplerResp) ProtoMessage() {}\n\nfunc (x *ChangeSamplerResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[11]\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 ChangeSamplerResp.ProtoReflect.Descriptor instead.\nfunc (*ChangeSamplerResp) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{11}\n}\n\nvar File_petstore_proto protoreflect.FileDescriptor\n\nvar file_petstore_proto_rawDesc = []byte{\n\t0x0a, 0x0e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x12, 0x08, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x1a, 0x16, 0x67, 0x6f, 0x6f, 0x67,\n\t0x6c, 0x65, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x22, 0x59, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12,\n\t0x27, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11,\n\t0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x44, 0x61, 0x74,\n\t0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x23, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74,\n\t0x79, 0x70, 0x65, 0x2e, 0x44, 0x61, 0x74, 0x65, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x7f, 0x0a,\n\t0x03, 0x50, 0x65, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,\n\t0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,\n\t0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,\n\t0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72,\n\t0x65, 0x2e, 0x50, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12,\n\t0x2d, 0x0a, 0x08, 0x62, 0x69, 0x72, 0x74, 0x68, 0x64, 0x61, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28,\n\t0x0b, 0x32, 0x11, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e,\n\t0x44, 0x61, 0x74, 0x65, 0x52, 0x08, 0x62, 0x69, 0x72, 0x74, 0x68, 0x64, 0x61, 0x79, 0x22, 0x2f,\n\t0x0a, 0x0a, 0x41, 0x64, 0x64, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x12, 0x21, 0x0a, 0x04,\n\t0x70, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x65, 0x74,\n\t0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x65, 0x74, 0x52, 0x04, 0x70, 0x65, 0x74, 0x73, 0x22,\n\t0x1f, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10,\n\t0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x73,\n\t0x22, 0x32, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65,\n\t0x71, 0x12, 0x21, 0x0a, 0x04, 0x70, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,\n\t0x0d, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x65, 0x74, 0x52, 0x04,\n\t0x70, 0x65, 0x74, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65,\n\t0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x21, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,\n\t0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01,\n\t0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x44, 0x65, 0x6c,\n\t0x65, 0x74, 0x65, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x8c, 0x01, 0x0a, 0x0d,\n\t0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a,\n\t0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61,\n\t0x6d, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03,\n\t0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x65,\n\t0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0f,\n\t0x62, 0x69, 0x72, 0x74, 0x68, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18,\n\t0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65,\n\t0x2e, 0x44, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0e, 0x62, 0x69, 0x72, 0x74,\n\t0x68, 0x64, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x55, 0x0a, 0x07, 0x53, 0x61,\n\t0x6d, 0x70, 0x6c, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x53,\n\t0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,\n\t0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x56, 0x61, 0x6c, 0x75,\n\t0x65, 0x22, 0x3f, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x61, 0x6d, 0x70, 0x6c,\n\t0x65, 0x72, 0x52, 0x65, 0x71, 0x12, 0x2b, 0x0a, 0x07, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72,\n\t0x65, 0x2e, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x52, 0x07, 0x73, 0x61, 0x6d, 0x70, 0x6c,\n\t0x65, 0x72, 0x22, 0x13, 0x0a, 0x11, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x61, 0x6d, 0x70,\n\t0x6c, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x2a, 0x4f, 0x0a, 0x07, 0x50, 0x65, 0x74, 0x54, 0x79,\n\t0x70, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x54, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10,\n\t0x00, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x54, 0x43, 0x61, 0x6e, 0x69, 0x6e, 0x65, 0x10, 0x01, 0x12,\n\t0x0c, 0x0a, 0x08, 0x50, 0x54, 0x46, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x10, 0x02, 0x12, 0x0a, 0x0a,\n\t0x06, 0x50, 0x54, 0x42, 0x69, 0x72, 0x64, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x54, 0x52,\n\t0x65, 0x70, 0x74, 0x69, 0x6c, 0x65, 0x10, 0x04, 0x2a, 0x44, 0x0a, 0x0b, 0x53, 0x61, 0x6d, 0x70,\n\t0x6c, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x54, 0x55, 0x6e, 0x6b,\n\t0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x4e, 0x65, 0x76, 0x65,\n\t0x72, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x54, 0x41, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x10,\n\t0x02, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x10, 0x03, 0x32, 0xd0,\n\t0x02, 0x0a, 0x08, 0x50, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x41,\n\t0x64, 0x64, 0x50, 0x65, 0x74, 0x73, 0x12, 0x14, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72,\n\t0x65, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x70,\n\t0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x65, 0x74, 0x73, 0x52,\n\t0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50,\n\t0x65, 0x74, 0x73, 0x12, 0x17, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x55,\n\t0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x70,\n\t0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65,\n\t0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65,\n\t0x74, 0x65, 0x50, 0x65, 0x74, 0x73, 0x12, 0x17, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72,\n\t0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x1a,\n\t0x18, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74,\n\t0x65, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x38, 0x0a, 0x0a, 0x53,\n\t0x65, 0x61, 0x72, 0x63, 0x68, 0x50, 0x65, 0x74, 0x73, 0x12, 0x17, 0x2e, 0x70, 0x65, 0x74, 0x73,\n\t0x74, 0x6f, 0x72, 0x65, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x50, 0x65, 0x74, 0x73, 0x52,\n\t0x65, 0x71, 0x1a, 0x0d, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x65,\n\t0x74, 0x22, 0x00, 0x30, 0x01, 0x12, 0x4a, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53,\n\t0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72,\n\t0x65, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x52,\n\t0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x68,\n\t0x61, 0x6e, 0x67, 0x65, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x22,\n\t0x00, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,\n\t0x50, 0x61, 0x63, 0x6b, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x2f,\n\t0x47, 0x6f, 0x2d, 0x66, 0x6f, 0x72, 0x2d, 0x44, 0x65, 0x76, 0x4f, 0x70, 0x73, 0x2f, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_petstore_proto_rawDescOnce sync.Once\n\tfile_petstore_proto_rawDescData = file_petstore_proto_rawDesc\n)\n\nfunc file_petstore_proto_rawDescGZIP() []byte {\n\tfile_petstore_proto_rawDescOnce.Do(func() {\n\t\tfile_petstore_proto_rawDescData = protoimpl.X.CompressGZIP(file_petstore_proto_rawDescData)\n\t})\n\treturn file_petstore_proto_rawDescData\n}\n\nvar file_petstore_proto_enumTypes = make([]protoimpl.EnumInfo, 2)\nvar file_petstore_proto_msgTypes = make([]protoimpl.MessageInfo, 12)\nvar file_petstore_proto_goTypes = []interface{}{\n\t(PetType)(0),              // 0: petstore.PetType\n\t(SamplerType)(0),          // 1: petstore.SamplerType\n\t(*DateRange)(nil),         // 2: petstore.DateRange\n\t(*Pet)(nil),               // 3: petstore.Pet\n\t(*AddPetsReq)(nil),        // 4: petstore.AddPetsReq\n\t(*AddPetsResp)(nil),       // 5: petstore.AddPetsResp\n\t(*UpdatePetsReq)(nil),     // 6: petstore.UpdatePetsReq\n\t(*UpdatePetsResp)(nil),    // 7: petstore.UpdatePetsResp\n\t(*DeletePetsReq)(nil),     // 8: petstore.DeletePetsReq\n\t(*DeletePetsResp)(nil),    // 9: petstore.DeletePetsResp\n\t(*SearchPetsReq)(nil),     // 10: petstore.SearchPetsReq\n\t(*Sampler)(nil),           // 11: petstore.Sampler\n\t(*ChangeSamplerReq)(nil),  // 12: petstore.ChangeSamplerReq\n\t(*ChangeSamplerResp)(nil), // 13: petstore.ChangeSamplerResp\n\t(*date.Date)(nil),         // 14: google.type.Date\n}\nvar file_petstore_proto_depIdxs = []int32{\n\t14, // 0: petstore.DateRange.start:type_name -> google.type.Date\n\t14, // 1: petstore.DateRange.end:type_name -> google.type.Date\n\t0,  // 2: petstore.Pet.type:type_name -> petstore.PetType\n\t14, // 3: petstore.Pet.birthday:type_name -> google.type.Date\n\t3,  // 4: petstore.AddPetsReq.pets:type_name -> petstore.Pet\n\t3,  // 5: petstore.UpdatePetsReq.pets:type_name -> petstore.Pet\n\t0,  // 6: petstore.SearchPetsReq.types:type_name -> petstore.PetType\n\t2,  // 7: petstore.SearchPetsReq.birthdate_range:type_name -> petstore.DateRange\n\t1,  // 8: petstore.Sampler.type:type_name -> petstore.SamplerType\n\t11, // 9: petstore.ChangeSamplerReq.sampler:type_name -> petstore.Sampler\n\t4,  // 10: petstore.PetStore.AddPets:input_type -> petstore.AddPetsReq\n\t6,  // 11: petstore.PetStore.UpdatePets:input_type -> petstore.UpdatePetsReq\n\t8,  // 12: petstore.PetStore.DeletePets:input_type -> petstore.DeletePetsReq\n\t10, // 13: petstore.PetStore.SearchPets:input_type -> petstore.SearchPetsReq\n\t12, // 14: petstore.PetStore.ChangeSampler:input_type -> petstore.ChangeSamplerReq\n\t5,  // 15: petstore.PetStore.AddPets:output_type -> petstore.AddPetsResp\n\t7,  // 16: petstore.PetStore.UpdatePets:output_type -> petstore.UpdatePetsResp\n\t9,  // 17: petstore.PetStore.DeletePets:output_type -> petstore.DeletePetsResp\n\t3,  // 18: petstore.PetStore.SearchPets:output_type -> petstore.Pet\n\t13, // 19: petstore.PetStore.ChangeSampler:output_type -> petstore.ChangeSamplerResp\n\t15, // [15:20] is the sub-list for method output_type\n\t10, // [10:15] is the sub-list for method input_type\n\t10, // [10:10] is the sub-list for extension type_name\n\t10, // [10:10] is the sub-list for extension extendee\n\t0,  // [0:10] is the sub-list for field type_name\n}\n\nfunc init() { file_petstore_proto_init() }\nfunc file_petstore_proto_init() {\n\tif File_petstore_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_petstore_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DateRange); 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_petstore_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Pet); 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_petstore_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*AddPetsReq); 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_petstore_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*AddPetsResp); 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_petstore_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*UpdatePetsReq); 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_petstore_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*UpdatePetsResp); 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_petstore_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DeletePetsReq); 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_petstore_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DeletePetsResp); 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_petstore_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*SearchPetsReq); 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_petstore_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Sampler); 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_petstore_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ChangeSamplerReq); 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_petstore_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ChangeSamplerResp); 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_petstore_proto_rawDesc,\n\t\t\tNumEnums:      2,\n\t\t\tNumMessages:   12,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_petstore_proto_goTypes,\n\t\tDependencyIndexes: file_petstore_proto_depIdxs,\n\t\tEnumInfos:         file_petstore_proto_enumTypes,\n\t\tMessageInfos:      file_petstore_proto_msgTypes,\n\t}.Build()\n\tFile_petstore_proto = out.File\n\tfile_petstore_proto_rawDesc = nil\n\tfile_petstore_proto_goTypes = nil\n\tfile_petstore_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "chapter/13/petstore-provider/internal/client/proto/petstore.proto",
    "content": "syntax = \"proto3\";\n\npackage petstore;\n\noption go_package = \"github.com/PacktPublishing/Go-for-DevOps/proto\";\n\nimport \"google/type/date.proto\";\n\n// Desribes the type of pets.\nenum PetType {\n\t// The type was not set.\n\tPTUnknown = 0;\n\t// The pet is a canine.\n\tPTCanine = 1;\n\t// The pet is a feline.\n\tPTFeline = 2;\n\t// The pet is a bird.\n\tPTBird = 3;\n\t// The pet is a reptile.\n\tPTReptile = 4;\n}\n\n// Represents a range of dates.\nmessage DateRange {\n\t// When to start the range, this is inclusive.\n\tgoogle.type.Date start = 1;\n\t// When to end the range, this is exclusive.\n\tgoogle.type.Date end = 2;\n}\n\n// Represents a unique pet.\nmessage Pet {\n\t// A UUIDv4 for this pet. This can never be set on an AddPet().\n\tstring id = 1;\n\t// The name of the pet.\n\tstring name = 2;\n\t// The type of pet.\n\tPetType type = 3;\n\t// The pet's birthday.\n\tgoogle.type.Date birthday = 4;\n}\n\n// The request used to add a pets to the system.\nmessage AddPetsReq {\n\t// The pet information to add. Pet.id must not be set.\n\trepeated Pet pets = 1;\n}\n\n// The response do AddPets().\nmessage AddPetsResp {\n\t// The IDs of the pets that were added.\n\trepeated string ids = 1;\n}\n\n// The request used to update pets in the system.\nmessage UpdatePetsReq {\n\t// The pet information to update. Pet.id must be set.\n\trepeated Pet pets = 1;\n}\n\n// The response do UpdatePets().\nmessage UpdatePetsResp {}\n\n// Used to indicate which pets to delete. This is an all or nothing request.\nmessage DeletePetsReq {\n\t// The IDs of the pets to delete.\n\trepeated string ids = 1;\n}\n\n// The response to a DeletePet().\nmessage DeletePetsResp{}\n\n// The request to search for pets.\nmessage SearchPetsReq {\n\t// Pet names to filter by.\n\trepeated string names = 1;\n\t// Pet types to filter by.\n\trepeated PetType types = 2;\n\t// Birthdays to filter by.\n\tDateRange birthdate_range = 3;\n}\n\n// Types of OTEL sampling we support.\nenum SamplerType {\n\tSTUnknown = 0;\n\tSTNever = 1;\n\tSTAlways = 2;\n\tSTFloat = 3;\n}\n\nmessage Sampler {\n\t// The type of sampling to change to.\n\tSamplerType type = 1;\n\n\t// This is the sampling rate if type == STFloat. Values must be\n\t// > 0 and <= 1.0 .\t\n\tdouble float_value = 2;\n}\n\n// Used to request we change the OTEL sampling.\nmessage ChangeSamplerReq {\n\tSampler sampler = 1;\n}\n\n// The response to a sampling change.\nmessage ChangeSamplerResp{}\n\nservice PetStore {\n\t// Adds pets to the pet store.\n\trpc AddPets(AddPetsReq) returns (AddPetsResp) {};\n\t// Updates pets entries in the store.\n\trpc UpdatePets(UpdatePetsReq) returns (UpdatePetsResp) {};\n\t// Deletes pets from the pet store.\n\trpc DeletePets(DeletePetsReq) returns (DeletePetsResp) {};\n\t// Finds pets in the pet store.\n\trpc SearchPets(SearchPetsReq) returns (stream Pet) {};\n\n\n\t// These are for management. In real life I might break this into a new server that is\n\t// serving on SSH so that it has to be auth'd and reachable only from in my network\n\t// or only if auth'd by something.\n\n\t// Changes the OTEL sampling type.\n\trpc ChangeSampler(ChangeSamplerReq) returns (ChangeSamplerResp) {};\n}\n"
  },
  {
    "path": "chapter/13/petstore-provider/internal/client/proto/petstore_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n\npackage proto\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// PetStoreClient is the client API for PetStore 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 PetStoreClient interface {\n\t// Adds pets to the pet store.\n\tAddPets(ctx context.Context, in *AddPetsReq, opts ...grpc.CallOption) (*AddPetsResp, error)\n\t// Updates pets entries in the store.\n\tUpdatePets(ctx context.Context, in *UpdatePetsReq, opts ...grpc.CallOption) (*UpdatePetsResp, error)\n\t// Deletes pets from the pet store.\n\tDeletePets(ctx context.Context, in *DeletePetsReq, opts ...grpc.CallOption) (*DeletePetsResp, error)\n\t// Finds pets in the pet store.\n\tSearchPets(ctx context.Context, in *SearchPetsReq, opts ...grpc.CallOption) (PetStore_SearchPetsClient, error)\n\t// Changes the OTEL sampling type.\n\tChangeSampler(ctx context.Context, in *ChangeSamplerReq, opts ...grpc.CallOption) (*ChangeSamplerResp, error)\n}\n\ntype petStoreClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewPetStoreClient(cc grpc.ClientConnInterface) PetStoreClient {\n\treturn &petStoreClient{cc}\n}\n\nfunc (c *petStoreClient) AddPets(ctx context.Context, in *AddPetsReq, opts ...grpc.CallOption) (*AddPetsResp, error) {\n\tout := new(AddPetsResp)\n\terr := c.cc.Invoke(ctx, \"/petstore.PetStore/AddPets\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *petStoreClient) UpdatePets(ctx context.Context, in *UpdatePetsReq, opts ...grpc.CallOption) (*UpdatePetsResp, error) {\n\tout := new(UpdatePetsResp)\n\terr := c.cc.Invoke(ctx, \"/petstore.PetStore/UpdatePets\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *petStoreClient) DeletePets(ctx context.Context, in *DeletePetsReq, opts ...grpc.CallOption) (*DeletePetsResp, error) {\n\tout := new(DeletePetsResp)\n\terr := c.cc.Invoke(ctx, \"/petstore.PetStore/DeletePets\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *petStoreClient) SearchPets(ctx context.Context, in *SearchPetsReq, opts ...grpc.CallOption) (PetStore_SearchPetsClient, error) {\n\tstream, err := c.cc.NewStream(ctx, &PetStore_ServiceDesc.Streams[0], \"/petstore.PetStore/SearchPets\", opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &petStoreSearchPetsClient{stream}\n\tif err := x.ClientStream.SendMsg(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := x.ClientStream.CloseSend(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn x, nil\n}\n\ntype PetStore_SearchPetsClient interface {\n\tRecv() (*Pet, error)\n\tgrpc.ClientStream\n}\n\ntype petStoreSearchPetsClient struct {\n\tgrpc.ClientStream\n}\n\nfunc (x *petStoreSearchPetsClient) Recv() (*Pet, error) {\n\tm := new(Pet)\n\tif err := x.ClientStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\nfunc (c *petStoreClient) ChangeSampler(ctx context.Context, in *ChangeSamplerReq, opts ...grpc.CallOption) (*ChangeSamplerResp, error) {\n\tout := new(ChangeSamplerResp)\n\terr := c.cc.Invoke(ctx, \"/petstore.PetStore/ChangeSampler\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// PetStoreServer is the server API for PetStore service.\n// All implementations must embed UnimplementedPetStoreServer\n// for forward compatibility\ntype PetStoreServer interface {\n\t// Adds pets to the pet store.\n\tAddPets(context.Context, *AddPetsReq) (*AddPetsResp, error)\n\t// Updates pets entries in the store.\n\tUpdatePets(context.Context, *UpdatePetsReq) (*UpdatePetsResp, error)\n\t// Deletes pets from the pet store.\n\tDeletePets(context.Context, *DeletePetsReq) (*DeletePetsResp, error)\n\t// Finds pets in the pet store.\n\tSearchPets(*SearchPetsReq, PetStore_SearchPetsServer) error\n\t// Changes the OTEL sampling type.\n\tChangeSampler(context.Context, *ChangeSamplerReq) (*ChangeSamplerResp, error)\n\tmustEmbedUnimplementedPetStoreServer()\n}\n\n// UnimplementedPetStoreServer must be embedded to have forward compatible implementations.\ntype UnimplementedPetStoreServer struct {\n}\n\nfunc (UnimplementedPetStoreServer) AddPets(context.Context, *AddPetsReq) (*AddPetsResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method AddPets not implemented\")\n}\nfunc (UnimplementedPetStoreServer) UpdatePets(context.Context, *UpdatePetsReq) (*UpdatePetsResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method UpdatePets not implemented\")\n}\nfunc (UnimplementedPetStoreServer) DeletePets(context.Context, *DeletePetsReq) (*DeletePetsResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method DeletePets not implemented\")\n}\nfunc (UnimplementedPetStoreServer) SearchPets(*SearchPetsReq, PetStore_SearchPetsServer) error {\n\treturn status.Errorf(codes.Unimplemented, \"method SearchPets not implemented\")\n}\nfunc (UnimplementedPetStoreServer) ChangeSampler(context.Context, *ChangeSamplerReq) (*ChangeSamplerResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ChangeSampler not implemented\")\n}\nfunc (UnimplementedPetStoreServer) mustEmbedUnimplementedPetStoreServer() {}\n\n// UnsafePetStoreServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to PetStoreServer will\n// result in compilation errors.\ntype UnsafePetStoreServer interface {\n\tmustEmbedUnimplementedPetStoreServer()\n}\n\nfunc RegisterPetStoreServer(s grpc.ServiceRegistrar, srv PetStoreServer) {\n\ts.RegisterService(&PetStore_ServiceDesc, srv)\n}\n\nfunc _PetStore_AddPets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(AddPetsReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(PetStoreServer).AddPets(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/petstore.PetStore/AddPets\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(PetStoreServer).AddPets(ctx, req.(*AddPetsReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _PetStore_UpdatePets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(UpdatePetsReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(PetStoreServer).UpdatePets(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/petstore.PetStore/UpdatePets\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(PetStoreServer).UpdatePets(ctx, req.(*UpdatePetsReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _PetStore_DeletePets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(DeletePetsReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(PetStoreServer).DeletePets(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/petstore.PetStore/DeletePets\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(PetStoreServer).DeletePets(ctx, req.(*DeletePetsReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _PetStore_SearchPets_Handler(srv interface{}, stream grpc.ServerStream) error {\n\tm := new(SearchPetsReq)\n\tif err := stream.RecvMsg(m); err != nil {\n\t\treturn err\n\t}\n\treturn srv.(PetStoreServer).SearchPets(m, &petStoreSearchPetsServer{stream})\n}\n\ntype PetStore_SearchPetsServer interface {\n\tSend(*Pet) error\n\tgrpc.ServerStream\n}\n\ntype petStoreSearchPetsServer struct {\n\tgrpc.ServerStream\n}\n\nfunc (x *petStoreSearchPetsServer) Send(m *Pet) error {\n\treturn x.ServerStream.SendMsg(m)\n}\n\nfunc _PetStore_ChangeSampler_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ChangeSamplerReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(PetStoreServer).ChangeSampler(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/petstore.PetStore/ChangeSampler\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(PetStoreServer).ChangeSampler(ctx, req.(*ChangeSamplerReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// PetStore_ServiceDesc is the grpc.ServiceDesc for PetStore 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 PetStore_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"petstore.PetStore\",\n\tHandlerType: (*PetStoreServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"AddPets\",\n\t\t\tHandler:    _PetStore_AddPets_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"UpdatePets\",\n\t\t\tHandler:    _PetStore_UpdatePets_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"DeletePets\",\n\t\t\tHandler:    _PetStore_DeletePets_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ChangeSampler\",\n\t\t\tHandler:    _PetStore_ChangeSampler_Handler,\n\t\t},\n\t},\n\tStreams: []grpc.StreamDesc{\n\t\t{\n\t\t\tStreamName:    \"SearchPets\",\n\t\t\tHandler:       _PetStore_SearchPets_Handler,\n\t\t\tServerStreams: true,\n\t\t},\n\t},\n\tMetadata: \"petstore.proto\",\n}\n"
  },
  {
    "path": "chapter/13/petstore-provider/internal/data_source_pet.go",
    "content": "package petstore\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/hashicorp/terraform-plugin-sdk/v2/diag\"\n\t\"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema\"\n\t\"github.com/pkg/errors\"\n\t\"google.golang.org/genproto/googleapis/type/date\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/13/petstore-provider/internal/client\"\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/13/petstore-provider/internal/client/proto\"\n)\n\nfunc dataSourcePet() *schema.Resource {\n\treturn &schema.Resource{\n\t\tReadContext: dataSourcePetRead,\n\t\tSchema:      getPetDataSchema(),\n\t}\n}\n\n// clientFromMeta casts meta into a Pet Store client or returns an error\nfunc clientFromMeta(meta interface{}) (*client.Client, error) {\n\tpsClient, ok := meta.(*client.Client)\n\tif !ok {\n\t\treturn nil, errors.New(\"meta does not contain a Pet Store client\")\n\t}\n\n\treturn psClient, nil\n}\n\n// dataSourcePetRead finds pets in the pet store given an ID\nfunc dataSourcePetRead(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {\n\tpsClient, err := clientFromMeta(meta)\n\tif err != nil {\n\t\treturn diag.FromErr(err)\n\t}\n\n\tpets, err := findPetsInStore(ctx, psClient, findPetsRequest{\n\t\tName:     data.Get(\"name\").(string),\n\t\tBirthday: data.Get(\"birthday\").(string),\n\t\tType:     PetType(data.Get(\"type\").(string)),\n\t\tID:       data.Get(\"pet_id\").(string),\n\t})\n\tif err != nil {\n\t\treturn diag.FromErr(err)\n\t}\n\n\t// always run\n\tdata.SetId(strconv.FormatInt(time.Now().Unix(), 10))\n\n\tif err := data.Set(\"pets\", flattenPets(pets)); err != nil {\n\t\treturn diag.FromErr(err)\n\t}\n\n\treturn nil\n}\n\nfunc flattenPets(pets []*client.Pet) []interface{} {\n\tifacePets := make([]interface{}, len(pets), len(pets))\n\tfor i, pet := range pets {\n\t\tifacePets[i] = petToMap(pet)\n\t}\n\treturn ifacePets\n}\n\n// setDataFromPet populates the resource data from the pet\nfunc setDataFromPet(pet *client.Pet, data *schema.ResourceData) diag.Diagnostics {\n\tpetMap := petToMap(pet)\n\tvar diags diag.Diagnostics\n\n\tfor k, v := range petMap {\n\t\tif err := data.Set(k, v); err != nil {\n\t\t\tdiags = append(diags, diag.Errorf(\"failed to set %s: %s\", k, err)...)\n\t\t}\n\t}\n\n\treturn diags\n}\n\nfunc petToMap(pet *client.Pet) map[string]interface{} {\n\treturn map[string]interface{}{\n\t\t\"name\":     pet.Name,\n\t\t\"type\":     string(protoPetTypeToPetType(pet.Type)),\n\t\t\"birthday\": pet.Birthday().Format(\"2006-01-02T15:04:05Z07:00\"),\n\t\t\"id\":       pet.Id,\n\t}\n}\n\n// fillPetFromData populates a pet from resource data\nfunc fillPetFromData(pet *client.Pet, data *schema.ResourceData) diag.Diagnostics {\n\tvar diags diag.Diagnostics\n\n\tpet.Pet.Id = data.Id()\n\tpet.Pet.Name = data.Get(\"name\").(string)\n\tpet.Pet.Type = petTypeToProtoPetType(PetType(data.Get(\"type\").(string)))\n\n\tif bday, ok := data.Get(\"birthday\").(string); ok {\n\t\tt, err := time.Parse(time.RFC3339, bday)\n\t\tif err != nil {\n\t\t\tdiags = append(diags, diag.FromErr(err)...)\n\t\t}\n\t\tpet.Pet.Birthday = timeToPbDate(t)\n\t}\n\n\treturn diags\n}\n\nfunc timeToPbDate(t time.Time) *date.Date {\n\treturn &date.Date{\n\t\tYear:  int32(t.Year()),\n\t\tMonth: int32(t.Month()),\n\t\tDay:   int32(t.Day()),\n\t}\n}\n\ntype findPetsRequest struct {\n\tID       string\n\tName     string\n\tType     PetType\n\tBirthday string\n}\n\n// findPetInStore searches the pet store for a pet that matches the custom resource pet.\nfunc findPetsInStore(ctx context.Context, psClient *client.Client, req findPetsRequest) ([]*client.Pet, error) {\n\tsearchReq := &pb.SearchPetsReq{}\n\tif string(req.Type) != \"\" {\n\t\tsearchReq.Types = []pb.PetType{petTypeToProtoPetType(req.Type)}\n\t}\n\n\tif req.Name != \"\" {\n\t\tsearchReq.Names = []string{req.Name}\n\t}\n\n\tpetsChan, err := psClient.SearchPets(ctx, searchReq)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed searching for pet\")\n\t}\n\n\tvar pets []*client.Pet\n\tfor pet := range petsChan {\n\t\tpet := pet\n\t\tif pet.Error() != nil {\n\t\t\treturn nil, pet.Error()\n\t\t}\n\n\t\tif req.ID == \"\" || pet.Id == req.ID {\n\t\t\tpets = append(pets, &pet)\n\t\t}\n\t}\n\n\treturn pets, nil\n}\n\nfunc petTypeToProtoPetType(petType PetType) pb.PetType {\n\tswitch petType {\n\tcase DogPetType:\n\t\treturn pb.PetType_PTCanine\n\tcase CatPetType:\n\t\treturn pb.PetType_PTFeline\n\tcase BirdPetType:\n\t\treturn pb.PetType_PTBird\n\tdefault:\n\t\treturn pb.PetType_PTReptile\n\t}\n}\n\nfunc protoPetTypeToPetType(pbPetType pb.PetType) PetType {\n\tswitch pbPetType {\n\tcase pb.PetType_PTCanine:\n\t\treturn DogPetType\n\tcase pb.PetType_PTFeline:\n\t\treturn CatPetType\n\tcase pb.PetType_PTBird:\n\t\treturn BirdPetType\n\tdefault:\n\t\treturn ReptilePetType\n\t}\n}\n"
  },
  {
    "path": "chapter/13/petstore-provider/internal/provider.go",
    "content": "package petstore\n\nimport (\n\t\"context\"\n\n\t\"github.com/hashicorp/terraform-plugin-sdk/v2/diag\"\n\t\"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/13/petstore-provider/internal/client\"\n)\n\n// Provider is the entry point for defining the Terraform provider, and will create a new Pet Store provider.\nfunc Provider() *schema.Provider {\n\treturn &schema.Provider{\n\t\tSchema: map[string]*schema.Schema{\n\t\t\t\"host\": {\n\t\t\t\tType:        schema.TypeString,\n\t\t\t\tOptional:    true,\n\t\t\t\tDefaultFunc: schema.EnvDefaultFunc(\"PETSTORE_HOST\", nil),\n\t\t\t},\n\t\t},\n\t\tResourcesMap: map[string]*schema.Resource{\n\t\t\t\"petstore_pet\": resourcePet(),\n\t\t},\n\t\tDataSourcesMap: map[string]*schema.Resource{\n\t\t\t\"petstore_pet\": dataSourcePet(),\n\t\t},\n\t\tConfigureContextFunc: configure,\n\t}\n}\n\n// configure builds a new Pet Store client the provider will use to interact with the Pet Store service\nfunc configure(_ context.Context, data *schema.ResourceData) (interface{}, diag.Diagnostics) {\n\t// Warning or errors can be collected in a slice type\n\tvar diags diag.Diagnostics\n\n\thost, ok := data.Get(\"host\").(string)\n\tif !ok {\n\t\treturn nil, diag.Errorf(\"the host (127.0.0.1:443) must be provided explicitly or via env var PETSTORE_HOST\")\n\t}\n\n\tc, err := client.New(host)\n\tif err != nil {\n\t\treturn nil, append(diags, diag.Diagnostic{\n\t\t\tSeverity: diag.Error,\n\t\t\tSummary:  \"Unable to create Pet Store client\",\n\t\t\tDetail:   \"Unable to connect to the Pet Store service\",\n\t\t})\n\t}\n\n\treturn c, diags\n}\n"
  },
  {
    "path": "chapter/13/petstore-provider/internal/resource_pets.go",
    "content": "package petstore\n\nimport (\n\t\"context\"\n\n\t\"github.com/hashicorp/terraform-plugin-sdk/v2/diag\"\n\t\"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/13/petstore-provider/internal/client\"\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/13/petstore-provider/internal/client/proto\"\n)\n\nfunc resourcePet() *schema.Resource {\n\treturn &schema.Resource{\n\t\tCreateContext: resourcePetCreate,\n\t\tReadContext:   resourcePetRead,\n\t\tUpdateContext: resourcePetUpdate,\n\t\tDeleteContext: resourcePetDelete,\n\t\tSchema:        getPetResourceSchema(),\n\t\tImporter: &schema.ResourceImporter{\n\t\t\tStateContext: schema.ImportStatePassthroughContext,\n\t\t},\n\t}\n}\n\n// resourcePetCreate creates a pet in the pet store\nfunc resourcePetCreate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {\n\tpsClient, err := clientFromMeta(meta)\n\tif err != nil {\n\t\treturn diag.FromErr(err)\n\t}\n\n\tpet := &client.Pet{Pet: &pb.Pet{}}\n\tdiags := fillPetFromData(pet, data)\n\tids, err := psClient.AddPets(ctx, []*pb.Pet{pet.Pet})\n\tif err != nil {\n\t\treturn append(diags, diag.FromErr(err)...)\n\t}\n\n\tdata.SetId(ids[0])\n\treturn diags\n}\n\n// resourcePetRead finds a pet in the pet store by ID and populate the resource data\nfunc resourcePetRead(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {\n\tpsClient, err := clientFromMeta(meta)\n\tif err != nil {\n\t\treturn diag.FromErr(err)\n\t}\n\n\tpets, err := findPetsInStore(ctx, psClient, findPetsRequest{ID: data.Id()})\n\tif err != nil {\n\t\treturn diag.FromErr(err)\n\t}\n\n\tif len(pets) == 0 {\n\t\treturn nil\n\t}\n\n\treturn setDataFromPet(pets[0], data)\n}\n\n// resourcePetUpdate updates a pet in the pet store by ID\nfunc resourcePetUpdate(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {\n\tpsClient, err := clientFromMeta(meta)\n\tif err != nil {\n\t\treturn diag.FromErr(err)\n\t}\n\n\tpets, err := findPetsInStore(ctx, psClient, findPetsRequest{ID: data.Id()})\n\tif err != nil {\n\t\treturn diag.FromErr(err)\n\t}\n\n\tif len(pets) == 0 {\n\t\treturn diag.Diagnostics{\n\t\t\t{\n\t\t\t\tSeverity: diag.Error,\n\t\t\t\tSummary:  \"no pet was found\",\n\t\t\t\tDetail:   \"no pet was found when trying to update the pet by ID\",\n\t\t\t},\n\t\t}\n\t}\n\n\tpet := pets[0]\n\tdiags := fillPetFromData(pet, data)\n\tif diags.HasError() {\n\t\treturn diags\n\t}\n\n\tif err := psClient.UpdatePets(ctx, []*pb.Pet{pet.Pet}); err != nil {\n\t\treturn append(diags, diag.FromErr(err)...)\n\t}\n\n\treturn diags\n}\n\n// resourcePetDelete deletes a pet in teh pet store by ID\nfunc resourcePetDelete(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {\n\tpsClient, err := clientFromMeta(meta)\n\tif err != nil {\n\t\treturn diag.FromErr(err)\n\t}\n\n\tif err := psClient.DeletePets(ctx, []string{data.Id()}); err != nil {\n\t\treturn diag.FromErr(err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "chapter/13/petstore-provider/internal/schema.go",
    "content": "package petstore\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/hashicorp/go-cty/cty\"\n\t\"github.com/hashicorp/terraform-plugin-sdk/v2/diag\"\n\t\"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema\"\n\t\"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation\"\n)\n\ntype PetType string\n\nconst (\n\tDogPetType     PetType = \"dog\"\n\tCatPetType     PetType = \"cat\"\n\tBirdPetType    PetType = \"bird\"\n\tReptilePetType PetType = \"reptile\"\n)\n\nfunc getPetDataSchema() map[string]*schema.Schema {\n\treturn map[string]*schema.Schema{\n\t\t\"pet_id\": {\n\t\t\tType:     schema.TypeString,\n\t\t\tOptional: true,\n\t\t},\n\t\t\"name\": {\n\t\t\tType:             schema.TypeString,\n\t\t\tOptional:         true,\n\t\t\tValidateDiagFunc: validateName(),\n\t\t},\n\t\t\"type\": {\n\t\t\tType:             schema.TypeString,\n\t\t\tOptional:         true,\n\t\t\tValidateDiagFunc: validateType(),\n\t\t},\n\t\t\"birthday\": {\n\t\t\tType:             schema.TypeString,\n\t\t\tOptional:         true,\n\t\t\tValidateDiagFunc: validateBirthday(),\n\t\t},\n\t\t\"pets\": {\n\t\t\tType:     schema.TypeList,\n\t\t\tComputed: true,\n\t\t\tElem: &schema.Resource{\n\t\t\t\tSchema: map[string]*schema.Schema{\n\t\t\t\t\t\"id\": {\n\t\t\t\t\t\tType:     schema.TypeString,\n\t\t\t\t\t\tComputed: true,\n\t\t\t\t\t},\n\t\t\t\t\t\"name\": {\n\t\t\t\t\t\tType:     schema.TypeString,\n\t\t\t\t\t\tComputed: true,\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": {\n\t\t\t\t\t\tType:     schema.TypeString,\n\t\t\t\t\t\tComputed: true,\n\t\t\t\t\t},\n\t\t\t\t\t\"birthday\": {\n\t\t\t\t\t\tType:     schema.TypeString,\n\t\t\t\t\t\tComputed: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc getPetResourceSchema() map[string]*schema.Schema {\n\treturn map[string]*schema.Schema{\n\t\t\"id\": {\n\t\t\tType:     schema.TypeString,\n\t\t\tOptional: true,\n\t\t\tComputed: true,\n\t\t},\n\t\t\"name\": {\n\t\t\tType:             schema.TypeString,\n\t\t\tRequired:         true,\n\t\t\tValidateDiagFunc: validateName(),\n\t\t},\n\t\t\"type\": {\n\t\t\tType:             schema.TypeString,\n\t\t\tRequired:         true,\n\t\t\tValidateDiagFunc: validateType(),\n\t\t},\n\t\t\"birthday\": {\n\t\t\tType:             schema.TypeString,\n\t\t\tRequired:         true,\n\t\t\tValidateDiagFunc: validateBirthday(),\n\t\t},\n\t}\n}\n\nfunc validateName() schema.SchemaValidateDiagFunc {\n\treturn validateDiagFunc(validation.All(validation.StringIsNotEmpty, validation.StringIsNotWhiteSpace))\n}\n\nfunc validateType() schema.SchemaValidateDiagFunc {\n\treturn validateDiagFunc(validation.StringInSlice([]string{\n\t\tstring(DogPetType),\n\t\tstring(CatPetType),\n\t\tstring(ReptilePetType),\n\t\tstring(BirdPetType),\n\t}, true))\n}\n\nfunc validateBirthday() schema.SchemaValidateDiagFunc {\n\treturn validateDiagFunc(validation.IsRFC3339Time)\n}\n\nfunc validateDiagFunc(validateFunc func(interface{}, string) ([]string, []error)) schema.SchemaValidateDiagFunc {\n\treturn func(i interface{}, path cty.Path) diag.Diagnostics {\n\t\twarnings, errs := validateFunc(i, fmt.Sprintf(\"%+v\", path))\n\t\tvar diags diag.Diagnostics\n\t\tfor _, warning := range warnings {\n\t\t\tdiags = append(diags, diag.Diagnostic{\n\t\t\t\tSeverity: diag.Warning,\n\t\t\t\tSummary:  warning,\n\t\t\t})\n\t\t}\n\t\tfor _, err := range errs {\n\t\t\tdiags = append(diags, diag.Diagnostic{\n\t\t\t\tSeverity: diag.Error,\n\t\t\t\tSummary:  err.Error(),\n\t\t\t})\n\t\t}\n\t\treturn diags\n\t}\n}\n"
  },
  {
    "path": "chapter/13/petstore-provider/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema\"\n\t\"github.com/hashicorp/terraform-plugin-sdk/v2/plugin\"\n\n\tpetstore \"github.com/PacktPublishing/Go-for-DevOps/chapter/13/petstore-provider/internal\"\n)\n\nfunc main() {\n\tplugin.Serve(&plugin.ServeOpts{\n\t\tProviderFunc: func() *schema.Provider {\n\t\t\treturn petstore.Provider()\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "chapter/14/petstore-operator/.dockerignore",
    "content": "# More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file\n# Ignore build and test binaries.\nbin/\ntestbin/\n"
  },
  {
    "path": "chapter/14/petstore-operator/.gitignore",
    "content": "\n# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\nbin\ntestbin/*\n\n# Test binary, build with `go test -c`\n*.test\n\n# Output of the go coverage tool, specifically when used with LiteIDE\n*.out\n\n# Kubernetes Generated files - skip generated files, except for vendored files\n\n!vendor/**/zz_generated.*\n\n# editor and IDE paraphernalia\n.idea\n*.swp\n*.swo\n*~\n"
  },
  {
    "path": "chapter/14/petstore-operator/Dockerfile",
    "content": "# Build the manager binary\nFROM golang:1.17 as builder\n\nWORKDIR /workspace\n# Copy the Go Modules manifests\nCOPY go.mod go.mod\nCOPY go.sum go.sum\n# cache deps before building and copying source so that we don't need to re-download as much\n# and so that source changes don't invalidate our downloaded layer\nRUN go mod download\n\n# Copy the go source\nCOPY main.go main.go\nCOPY api/ api/\nCOPY controllers/ controllers/\nCOPY client/ client/\n\n# Build\nRUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go\n\n# Use distroless as minimal base image to package the manager binary\n# Refer to https://github.com/GoogleContainerTools/distroless for more details\nFROM gcr.io/distroless/static:nonroot\nWORKDIR /\nCOPY --from=builder /workspace/manager .\nUSER 65532:65532\n\nENTRYPOINT [\"/manager\"]\n"
  },
  {
    "path": "chapter/14/petstore-operator/Makefile",
    "content": "# VERSION defines the project version for the bundle.\n# Update this value when you upgrade the version of your project.\n# To re-generate a bundle for another specific version without changing the standard setup, you can:\n# - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2)\n# - use environment variables to overwrite this value (e.g export VERSION=0.0.2)\nVERSION ?= 0.0.1\n\n# CHANNELS define the bundle channels used in the bundle.\n# Add a new line here if you would like to change its default config. (E.g CHANNELS = \"candidate,fast,stable\")\n# To re-generate a bundle for other specific channels without changing the standard setup, you can:\n# - use the CHANNELS as arg of the bundle target (e.g make bundle CHANNELS=candidate,fast,stable)\n# - use environment variables to overwrite this value (e.g export CHANNELS=\"candidate,fast,stable\")\nifneq ($(origin CHANNELS), undefined)\nBUNDLE_CHANNELS := --channels=$(CHANNELS)\nendif\n\n# DEFAULT_CHANNEL defines the default channel used in the bundle.\n# Add a new line here if you would like to change its default config. (E.g DEFAULT_CHANNEL = \"stable\")\n# To re-generate a bundle for any other default channel without changing the default setup, you can:\n# - use the DEFAULT_CHANNEL as arg of the bundle target (e.g make bundle DEFAULT_CHANNEL=stable)\n# - use environment variables to overwrite this value (e.g export DEFAULT_CHANNEL=\"stable\")\nifneq ($(origin DEFAULT_CHANNEL), undefined)\nBUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL)\nendif\nBUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL)\n\n# IMAGE_TAG_BASE defines the docker.io namespace and part of the image name for remote images.\n# This variable is used to construct full image tags for bundle and catalog images.\n#\n# For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both\n# example.com/petstore-operator-bundle:$VERSION and example.com/petstore-operator-catalog:$VERSION.\nIMAGE_TAG_BASE ?= example.com/petstore-operator\n\n# BUNDLE_IMG defines the image:tag used for the bundle.\n# You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=<some-registry>/<project-name-bundle>:<tag>)\nBUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION)\n\n# BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command\nBUNDLE_GEN_FLAGS ?= -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)\n\n# USE_IMAGE_DIGESTS defines if images are resolved via tags or digests\n# You can enable this value if you would like to use SHA Based Digests\n# To enable set flag to true\nUSE_IMAGE_DIGESTS ?= false\nifeq ($(USE_IMAGE_DIGESTS), true)\n\tBUNDLE_GEN_FLAGS += --use-image-digests\nendif\n\n# Image URL to use all building/pushing image targets\nIMG ?= controller:latest\n# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.\nENVTEST_K8S_VERSION = 1.23\n\n# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)\nifeq (,$(shell go env GOBIN))\nGOBIN=$(shell go env GOPATH)/bin\nelse\nGOBIN=$(shell go env GOBIN)\nendif\n\n# Setting SHELL to bash allows bash commands to be executed by recipes.\n# This is a requirement for 'setup-envtest.sh' in the test target.\n# Options are set to exit when a recipe line exits non-zero or a piped command fails.\nSHELL = /usr/bin/env bash -o pipefail\n.SHELLFLAGS = -ec\n\n.PHONY: all\nall: build\n\n##@ General\n\n# The help target prints out all targets with their descriptions organized\n# beneath their categories. The categories are represented by '##@' and the\n# target descriptions by '##'. The awk commands is responsible for reading the\n# entire set of makefiles included in this invocation, looking for lines of the\n# file as xyz: ## something, and then pretty-format the target and help. Then,\n# if there's a line with ##@ something, that gets pretty-printed as a category.\n# More info on the usage of ANSI control characters for terminal formatting:\n# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters\n# More info on the awk command:\n# http://linuxcommand.org/lc3_adv_awk.php\n\n.PHONY: help\nhelp: ## Display this help.\n\t@awk 'BEGIN {FS = \":.*##\"; printf \"\\nUsage:\\n  make \\033[36m<target>\\033[0m\\n\"} /^[a-zA-Z_0-9-]+:.*?##/ { printf \"  \\033[36m%-15s\\033[0m %s\\n\", $$1, $$2 } /^##@/ { printf \"\\n\\033[1m%s\\033[0m\\n\", substr($$0, 5) } ' $(MAKEFILE_LIST)\n\n##@ Development\n\n.PHONY: manifests\nmanifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.\n\t$(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths=\"./...\" output:crd:artifacts:config=config/crd/bases\n\n.PHONY: generate\ngenerate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.\n\t$(CONTROLLER_GEN) object:headerFile=\"hack/boilerplate.go.txt\" paths=\"./...\"\n\n.PHONY: fmt\nfmt: ## Run go fmt against code.\n\tgo fmt ./...\n\n.PHONY: vet\nvet: ## Run go vet against code.\n\tgo vet ./...\n\n.PHONY: test\ntest: manifests generate fmt vet envtest ## Run tests.\n\tKUBEBUILDER_ASSETS=\"$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)\" go test ./... -coverprofile cover.out\n\n##@ Build\n\n.PHONY: build\nbuild: generate fmt vet ## Build manager binary.\n\tgo build -o bin/manager main.go\n\n.PHONY: run\nrun: manifests generate fmt vet ## Run a controller from your host.\n\tgo run ./main.go\n\n.PHONY: docker-build\ndocker-build: test ## Build docker image with the manager.\n\tdocker build -t ${IMG} .\n\n.PHONY: docker-push\ndocker-push: ## Push docker image with the manager.\n\tdocker push ${IMG}\n\n##@ Deployment\n\nifndef ignore-not-found\n  ignore-not-found = false\nendif\n\n.PHONY: install\ninstall: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config.\n\t$(KUSTOMIZE) build config/crd | kubectl apply -f -\n\n.PHONY: uninstall\nuninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.\n\t$(KUSTOMIZE) build config/crd | kubectl delete --ignore-not-found=$(ignore-not-found) -f -\n\n.PHONY: deploy\ndeploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.\n\tcd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}\n\t$(KUSTOMIZE) build config/default | kubectl apply -f -\n\n.PHONY: undeploy\nundeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.\n\t$(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f -\n\nCONTROLLER_GEN = $(shell pwd)/bin/controller-gen\n.PHONY: controller-gen\ncontroller-gen: ## Download controller-gen locally if necessary.\n\t$(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.8.0)\n\nKUSTOMIZE = $(shell pwd)/bin/kustomize\n.PHONY: kustomize\nkustomize: ## Download kustomize locally if necessary.\n\t$(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7)\n\nENVTEST = $(shell pwd)/bin/setup-envtest\n.PHONY: envtest\nenvtest: ## Download envtest-setup locally if necessary.\n\t$(call go-get-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest)\n\n# go-get-tool will 'go get' any package $2 and install it to $1.\nPROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))\ndefine go-get-tool\n@[ -f $(1) ] || { \\\nset -e ;\\\nTMP_DIR=$$(mktemp -d) ;\\\ncd $$TMP_DIR ;\\\ngo mod init tmp ;\\\necho \"Downloading $(2)\" ;\\\nGOBIN=$(PROJECT_DIR)/bin go get $(2) ;\\\nrm -rf $$TMP_DIR ;\\\n}\nendef\n\n.PHONY: bundle\nbundle: manifests kustomize ## Generate bundle manifests and metadata, then validate generated files.\n\toperator-sdk generate kustomize manifests -q\n\tcd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG)\n\t$(KUSTOMIZE) build config/manifests | operator-sdk generate bundle $(BUNDLE_GEN_FLAGS)\n\toperator-sdk bundle validate ./bundle\n\n.PHONY: bundle-build\nbundle-build: ## Build the bundle image.\n\tdocker build -f bundle.Dockerfile -t $(BUNDLE_IMG) .\n\n.PHONY: bundle-push\nbundle-push: ## Push the bundle image.\n\t$(MAKE) docker-push IMG=$(BUNDLE_IMG)\n\n.PHONY: opm\nOPM = ./bin/opm\nopm: ## Download opm locally if necessary.\nifeq (,$(wildcard $(OPM)))\nifeq (,$(shell which opm 2>/dev/null))\n\t@{ \\\n\tset -e ;\\\n\tmkdir -p $(dir $(OPM)) ;\\\n\tOS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \\\n\tcurl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.19.1/$${OS}-$${ARCH}-opm ;\\\n\tchmod +x $(OPM) ;\\\n\t}\nelse\nOPM = $(shell which opm)\nendif\nendif\n\n# A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=example.com/operator-bundle:v0.1.0,example.com/operator-bundle:v0.2.0).\n# These images MUST exist in a registry and be pull-able.\nBUNDLE_IMGS ?= $(BUNDLE_IMG)\n\n# The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0).\nCATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION)\n\n# Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image.\nifneq ($(origin CATALOG_BASE_IMG), undefined)\nFROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG)\nendif\n\n# Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'.\n# This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see:\n# https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator\n.PHONY: catalog-build\ncatalog-build: opm ## Build a catalog image.\n\t$(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT)\n\n# Push the catalog image.\n.PHONY: catalog-push\ncatalog-push: ## Push a catalog image.\n\t$(MAKE) docker-push IMG=$(CATALOG_IMG)\n"
  },
  {
    "path": "chapter/14/petstore-operator/PROJECT",
    "content": "domain: example.com\nlayout:\n- go.kubebuilder.io/v3\nplugins:\n  manifests.sdk.operatorframework.io/v2: {}\n  scorecard.sdk.operatorframework.io/v2: {}\nprojectName: petstore-operator\nrepo: github.com/Go-for-DevOps/chapter/14/petstore-operator\nresources:\n- api:\n    crdVersion: v1\n    namespaced: true\n  controller: true\n  domain: example.com\n  group: petstore\n  kind: Pet\n  path: github.com/Go-for-DevOps/chapter/14/petstore-operator/api/v1alpha1\n  version: v1alpha1\nversion: \"3\"\n"
  },
  {
    "path": "chapter/14/petstore-operator/Tiltfile",
    "content": "# build and deploy the petstore service the controller will use to store pets\nk8s_yaml(\"./config/petstore-service/service.yaml\")\n# build and push the petstore service image to the local registry\ndocker_build(\"petstore:latest\", \"../../11/petstore/.\")\n\n# generate the controller manifest and deploy to the cluster\nk8s_yaml(kustomize(\"./config/default\"))\n# build and push the controller image to the local registry\ndocker_build(\"controller:latest\", \".\")\n"
  },
  {
    "path": "chapter/14/petstore-operator/api/v1alpha1/groupversion_info.go",
    "content": "/*\nCopyright 2022.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Package v1alpha1 contains API Schema definitions for the petstore v1alpha1 API group\n//+kubebuilder:object:generate=true\n//+groupName=petstore.example.com\npackage v1alpha1\n\nimport (\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"sigs.k8s.io/controller-runtime/pkg/scheme\"\n)\n\nvar (\n\t// GroupVersion is group version used to register these objects\n\tGroupVersion = schema.GroupVersion{Group: \"petstore.example.com\", Version: \"v1alpha1\"}\n\n\t// SchemeBuilder is used to add go types to the GroupVersionKind scheme\n\tSchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}\n\n\t// AddToScheme adds the types in this group-version to the given scheme.\n\tAddToScheme = SchemeBuilder.AddToScheme\n)\n"
  },
  {
    "path": "chapter/14/petstore-operator/api/v1alpha1/pet_types.go",
    "content": "/*\nCopyright 2022.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage v1alpha1\n\nimport (\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n)\n\n// PetType is the type of the pet. For example, a dog.\n// +kubebuilder:validation:Enum=dog;cat;bird;reptile\ntype PetType string\n\nconst (\n\tDogPetType     PetType = \"dog\"\n\tCatPetType     PetType = \"cat\"\n\tBirdPetType    PetType = \"bird\"\n\tReptilePetType PetType = \"reptile\"\n)\n\n// PetSpec defines the desired state of Pet\ntype PetSpec struct {\n\t// Name is the name of the pet\n\tName string `json:\"name\"`\n\t// Type is the type of pet\n\tType PetType `json:\"type\"`\n\t// Birthday is the date the pet was born\n\tBirthday metav1.Time `json:\"birthday\"`\n}\n\n// PetStatus defines the observed state of Pet\ntype PetStatus struct {\n\t// ID is the unique identifier created by the service for the pet\n\tID string `json:\"id,omitempty\"`\n}\n\n//+kubebuilder:object:root=true\n//+kubebuilder:subresource:status\n\n// Pet is the Schema for the pets API\ntype Pet struct {\n\tmetav1.TypeMeta   `json:\",inline\"`\n\tmetav1.ObjectMeta `json:\"metadata,omitempty\"`\n\n\tSpec   PetSpec   `json:\"spec,omitempty\"`\n\tStatus PetStatus `json:\"status,omitempty\"`\n}\n\n//+kubebuilder:object:root=true\n\n// PetList contains a list of Pet\ntype PetList struct {\n\tmetav1.TypeMeta `json:\",inline\"`\n\tmetav1.ListMeta `json:\"metadata,omitempty\"`\n\tItems           []Pet `json:\"items\"`\n}\n\nfunc init() {\n\tSchemeBuilder.Register(&Pet{}, &PetList{})\n}\n"
  },
  {
    "path": "chapter/14/petstore-operator/api/v1alpha1/zz_generated.deepcopy.go",
    "content": "//go:build !ignore_autogenerated\n// +build !ignore_autogenerated\n\n/*\nCopyright 2022.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by controller-gen. DO NOT EDIT.\n\npackage v1alpha1\n\nimport (\n\truntime \"k8s.io/apimachinery/pkg/runtime\"\n)\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *Pet) DeepCopyInto(out *Pet) {\n\t*out = *in\n\tout.TypeMeta = in.TypeMeta\n\tin.ObjectMeta.DeepCopyInto(&out.ObjectMeta)\n\tin.Spec.DeepCopyInto(&out.Spec)\n\tout.Status = in.Status\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Pet.\nfunc (in *Pet) DeepCopy() *Pet {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(Pet)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.\nfunc (in *Pet) DeepCopyObject() runtime.Object {\n\tif c := in.DeepCopy(); c != nil {\n\t\treturn c\n\t}\n\treturn nil\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *PetList) DeepCopyInto(out *PetList) {\n\t*out = *in\n\tout.TypeMeta = in.TypeMeta\n\tin.ListMeta.DeepCopyInto(&out.ListMeta)\n\tif in.Items != nil {\n\t\tin, out := &in.Items, &out.Items\n\t\t*out = make([]Pet, len(*in))\n\t\tfor i := range *in {\n\t\t\t(*in)[i].DeepCopyInto(&(*out)[i])\n\t\t}\n\t}\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PetList.\nfunc (in *PetList) DeepCopy() *PetList {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(PetList)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.\nfunc (in *PetList) DeepCopyObject() runtime.Object {\n\tif c := in.DeepCopy(); c != nil {\n\t\treturn c\n\t}\n\treturn nil\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *PetSpec) DeepCopyInto(out *PetSpec) {\n\t*out = *in\n\tin.Birthday.DeepCopyInto(&out.Birthday)\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PetSpec.\nfunc (in *PetSpec) DeepCopy() *PetSpec {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(PetSpec)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *PetStatus) DeepCopyInto(out *PetStatus) {\n\t*out = *in\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PetStatus.\nfunc (in *PetStatus) DeepCopy() *PetStatus {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(PetStatus)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n"
  },
  {
    "path": "chapter/14/petstore-operator/client/client.go",
    "content": "// Client provides an API client to the petstore service.\npackage client\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/14/petstore-operator/client/internal/server/storage\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/metadata\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/14/petstore-operator/client/proto\"\n)\n\n// Client is a client to the petstore service.\ntype Client struct {\n\tclient pb.PetStoreClient\n\tconn   *grpc.ClientConn\n}\n\n// New is the constructor for Client. addr is the server's [host]:[port].\nfunc New(addr string) (*Client, error) {\n\tconn, err := grpc.Dial(addr, grpc.WithInsecure())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Client{\n\t\tclient: pb.NewPetStoreClient(conn),\n\t\tconn:   conn,\n\t}, nil\n}\n\n// Pet is a wrapper around a *pb.Pet that can return Go versions of\n// fields and errors if the returned stream has an error.\ntype Pet struct {\n\t*pb.Pet\n\terr error\n}\n\n// Proto will give the Pet's proto representation.\nfunc (p Pet) Proto() *pb.Pet {\n\treturn p.Pet\n}\n\n// Birthday returns the Pet's birthday as a time.Time.\nfunc (p Pet) Birthday() time.Time {\n\t// We are ignoring the error as we will either get a zero time\n\t// anyways and the server should be preventing this problem.\n\tt, _ := storage.BirthdayToTime(context.Background(), p.Pet.Birthday)\n\treturn t\n}\n\n// Error indicates if there was an error in the Pet output stream.\nfunc (p Pet) Error() error {\n\treturn p.err\n}\n\n// CallOptions are optional options for an RPC call.\ntype CallOption func(co *callOptions)\n\ntype callOptions struct {\n\ttrace *string\n}\n\n// TraceID will cause the RPC call to execute a trace on the service and return \"s\" to the ID.\n// If s == nil, this will ignore the option. If \"s\" is not set after the call finishes, then\n// no trace was made.\nfunc TraceID(s *string) CallOption {\n\treturn func(co *callOptions) {\n\t\tif s == nil {\n\t\t\treturn\n\t\t}\n\t\tco.trace = s\n\t}\n}\n\n// AddPets adds pets to the service and returns their unique identities in the\n// same order as being added.\nfunc (c *Client) AddPets(ctx context.Context, pets []*pb.Pet, options ...CallOption) ([]string, error) {\n\tif len(pets) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tfor _, p := range pets {\n\t\tif err := storage.ValidatePet(ctx, p, false); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tvar header metadata.MD\n\tctx, gOpts, f := handleCallOptions(ctx, &header, options)\n\tdefer f()\n\n\tresp, err := c.client.AddPets(ctx, &pb.AddPetsReq{Pets: pets}, gOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn resp.Ids, nil\n}\n\n// UpdatePets updates pets that already exist in the system.\nfunc (c *Client) UpdatePets(ctx context.Context, pets []*pb.Pet, options ...CallOption) error {\n\tif len(pets) == 0 {\n\t\treturn nil\n\t}\n\n\tfor _, p := range pets {\n\t\tif err := storage.ValidatePet(ctx, p, true); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tvar header metadata.MD\n\tctx, gOpts, f := handleCallOptions(ctx, &header, options)\n\tdefer f()\n\n\t_, err := c.client.UpdatePets(ctx, &pb.UpdatePetsReq{Pets: pets}, gOpts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// DeletePets deletes pets with the IDs passed. If the ID doesn't exist, the\n// system ignores it.\nfunc (c *Client) DeletePets(ctx context.Context, ids []string, options ...CallOption) error {\n\tif len(ids) == 0 {\n\t\treturn nil\n\t}\n\n\tvar header metadata.MD\n\tctx, gOpts, f := handleCallOptions(ctx, &header, options)\n\tdefer f()\n\n\t_, err := c.client.DeletePets(ctx, &pb.DeletePetsReq{Ids: ids}, gOpts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// SearchPets searches the pet store for pets matching the filter. If the filter contains\n// no entries, then all pets will be returned.\nfunc (c *Client) SearchPets(ctx context.Context, filter *pb.SearchPetsReq, options ...CallOption) (chan Pet, error) {\n\tif filter == nil {\n\t\treturn nil, fmt.Errorf(\"the filter cannot be nil\")\n\t}\n\n\tvar header metadata.MD\n\tctx, gOpts, f := handleCallOptions(ctx, &header, options)\n\n\tstream, err := c.client.SearchPets(ctx, filter, gOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tch := make(chan Pet, 1)\n\tgo func() {\n\t\tdefer close(ch)\n\t\tdefer f()\n\n\t\tfor {\n\t\t\tp, err := stream.Recv()\n\t\t\tif err == io.EOF {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\tch <- Pet{err: err}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tch <- Pet{Pet: p}\n\t\t}\n\t}()\n\treturn ch, nil\n}\n\n// SamplerType is the type of OTEL sampling to do.\ntype SamplerType int32\n\nconst (\n\tSTUnknown SamplerType = 0\n\tNever     SamplerType = 1\n\tAlways    SamplerType = 2\n\tFloat     SamplerType = 3\n)\n\nvar validTypes = map[SamplerType]bool{\n\tNever:  true,\n\tAlways: true,\n\tFloat:  true,\n}\n\ntype Sampler struct {\n\t// Type is the type of sampling to use.\n\tType SamplerType\n\t// Rate is the sampling rate, only used if type is Float.\n\tRate float64\n}\n\nfunc (s *Sampler) validate() error {\n\tif !validTypes[s.Type] {\n\t\treturn fmt.Errorf(\"type %v is not a supported type\", s.Type)\n\t}\n\tif s.Type == Float {\n\t\tif s.Rate <= 0 || s.Rate > 1 {\n\t\t\treturn fmt.Errorf(\"Rate must be > 0 && <= 1.0, was %v\", s.Rate)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (s *Sampler) proto() *pb.Sampler {\n\treturn &pb.Sampler{\n\t\tType:       pb.SamplerType(s.Type),\n\t\tFloatValue: s.Rate,\n\t}\n}\n\nfunc (s *Sampler) fromProto(p *pb.Sampler) {\n\ts.Type = SamplerType(p.Type)\n\ts.Rate = p.FloatValue\n}\n\n// ChangeSampler changes the sampling type and rate on the server. This is\n// and admin function that in production should be restricted.\nfunc (c *Client) ChangeSampler(ctx context.Context, sc Sampler, options ...CallOption) error {\n\tif err := sc.validate(); err != nil {\n\t\treturn err\n\t}\n\n\tvar header metadata.MD\n\tctx, gOpts, f := handleCallOptions(ctx, &header, options)\n\tdefer f()\n\n\t_, err := c.client.ChangeSampler(ctx, &pb.ChangeSamplerReq{Sampler: sc.proto()}, gOpts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc handleCallOptions(ctx context.Context, header *metadata.MD, options []CallOption) (context.Context, []grpc.CallOption, func()) {\n\topts := callOptions{}\n\tfor _, o := range options {\n\t\to(&opts)\n\t}\n\tvar gOpts []grpc.CallOption\n\n\tif opts.trace != nil {\n\t\t(*header)[\"trace\"] = nil\n\t\tgOpts = append(gOpts, grpc.Header(header))\n\t}\n\n\tf := func() {\n\t\tif opts.trace != nil {\n\t\t\tif len((*header)[\"otel.traceID\"]) != 0 {\n\t\t\t\t*opts.trace = (*header)[\"otel.traceID\"][0]\n\t\t\t}\n\t\t}\n\t}\n\n\treturn ctx, gOpts, f\n}\n"
  },
  {
    "path": "chapter/14/petstore-operator/client/internal/server/errors/errors.go",
    "content": "// Package errors is a replacement for the golang standard library \"errors\". This replacement\n// adds errors to the Open Telemetry spans. The signatures only differs in that\n// New() now takes a context.Context object and fmt.Errorf() has been moved here and also takes a Context.Context.\npackage errors\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"go.opentelemetry.io/otel/codes\"\n\t\"go.opentelemetry.io/otel/trace\"\n)\n\n// New creates a new error and writes the error to a span if it exists in the context.\nfunc New(ctx context.Context, text string) error {\n\tspan := trace.SpanFromContext(ctx)\n\n\terr := errors.New(text)\n\tspan.RecordError(err)\n\tspan.SetStatus(codes.Error, err.Error())\n\n\treturn err\n}\n\n// Errorf implements fmt.Errorf with the addition of a Context that if it contains a span\n// will have the error added to the span.\nfunc Errorf(ctx context.Context, s string, i ...interface{}) error {\n\tspan := trace.SpanFromContext(ctx)\n\n\terr := fmt.Errorf(s, i...)\n\tspan.RecordError(err)\n\tspan.SetStatus(codes.Error, err.Error())\n\n\treturn err\n}\n\n// As implements errors.As().\nfunc As(err error, target interface{}) bool {\n\treturn errors.As(err, target)\n}\n\n// Is implements errors.Is().\nfunc Is(err, target error) bool {\n\treturn errors.Is(err, target)\n}\n\n// Unwrap implemements errors.Unwrap().\nfunc Unwrap(err error) error {\n\treturn errors.Unwrap(err)\n}\n"
  },
  {
    "path": "chapter/14/petstore-operator/client/internal/server/log/log.go",
    "content": "/*\nPackage log is a replacement for the standard library log package that\nlogs to OTEL spans contained in Context objects. These are seen as\nevents with the attribute \"log\" set to true.\n\nThe preferred way to log is to use an event:\n\tfunc someFunc(ctx context.Context) {\n\t\te := NewEvent(\"someFunc()\")\n\t\tdefer e.Done(ctx)\n\t\tstart := time.Now()\n\t\tdefer func() {\n\t\t\te.Add(\"latency.ns\", int(time.Since(start)))\n\t\t}()\n\t}\n\nThis records an event in the current span that has a key of \"latency.ns\" with the value in nano-seconds the operation took.\n\nYou can use this to log in a similar manner to the logging package with Println and Printf.  This is generally only useful for some generic debugging where you want to log something and filter the trace by messages with key \"log\". Generally these are messages you don't want to keep.\n\tfunc main() {\n\t\tctx := context.Background()\n\n\t\tlog.SetFlags(log.LstdFlags | log.Lshortfile)\n\n\t\tlog.Println(ctx, \"Starting main\")\n\n\t\tlog.Printf(ctx, \"Env variables: %v\", os.Environ())\n\t}\n\nThe above won't log anything, as there is no Span on the Context. If there\nwas it would get output to the Open Telementry provider.\n\nIf you need to use the standard library log, you can use Logger:\n\tlog.Logger.Println(\"hello world\")\n\nThis would print whever the stanard logger prints to. This defaults\nto the standard logger, but you can replace with another Logger if you wish.\n\nYou should only log messages with a standard logger when it can't be output to a trace. These are critical messages that indicate a definite bug. This keeps logging to only critical events and de-clutters what you need to look at to when doing a debug.\n*/\npackage log\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"runtime\"\n\t\"sync\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n)\n\nconst (\n\tLdate         = 1 << iota     // the date in the local time zone: 2009/01/23\n\tLtime                         // the time in the local time zone: 01:23:23\n\tLmicroseconds                 // microsecond resolution: 01:23:23.123123.  assumes Ltime.\n\tLlongfile                     // full file name and line number: /a/b/c/d.go:23\n\tLshortfile                    // final file name element and line number: d.go:23. overrides Llongfile\n\tLUTC                          // if Ldate or Ltime is set, use UTC rather than the local time zone\n\tLstdFlags     = Ldate | Ltime // initial values for the standard logger\n)\n\n// Logger provides access to the standard library's default logger.\n// This can be replaced in main with a logger of your choice.\nvar Logger *log.Logger = log.Default()\n\n// setup the standard logger with flags.\nvar std = &logger{flag: LstdFlags}\n\n// pool provides a pool of Event objects to keep our allocations to a minimum.\nvar pool = &eventPool{\n\tbuf: make(chan *Event, 100),\n\tpool: sync.Pool{\n\t\tNew: func() interface{} {\n\t\t\treturn &Event{}\n\t\t},\n\t},\n}\n\n// eventPool uses a set amount of Event objects and a sync.Pool for overflow.\n// Note: this would actually be a great place for metrics to key in on what would be\n// an optimal size for buf to prevent pool use.\ntype eventPool struct {\n\tbuf  chan *Event\n\tpool sync.Pool\n}\n\nfunc (e *eventPool) get() *Event {\n\tselect {\n\tcase ev := <-e.buf:\n\t\treturn ev\n\tdefault:\n\t}\n\treturn e.pool.Get().(*Event)\n}\n\nfunc (e *eventPool) put(ev *Event) {\n\tev.reset()\n\tselect {\n\tcase e.buf <- ev:\n\tdefault:\n\t}\n\te.pool.Put(ev)\n}\n\n// Event represents a named event that occurs. This is the prefered way to log data.\n// Events have attributes and those attributes are key/value pairs. You create\n// an event and stuff attributes using Add() until the event is over and call Done().\n// This will render the event to the current span. if no attrs exist, the event is ignored.\n// To avoid extra allocations\ntype Event struct {\n\tname  string\n\tattrs []attribute.KeyValue\n}\n\n// NewEvent returns a new Event.\nfunc NewEvent(name string) *Event {\n\tev := pool.get()\n\tev.name = name\n\treturn ev\n}\n\nfunc (e *Event) reset() {\n\te.name = \"\"\n\te.attrs = e.attrs[0:0]\n}\n\n// Add adds an attribute named k with value i. i can be: bool, []bool, float64, []float64, int, []int, int64, []int64, string and []string.\n// If the value isn't one of those values, a standard log message is printed indicating a bug.\nfunc (e *Event) Add(k string, i interface{}) {\n\tif e.name == \"\" {\n\t\treturn\n\t}\n\tswitch v := i.(type) {\n\tcase bool:\n\t\te.attrs = append(e.attrs, attribute.Bool(k, v))\n\tcase []bool:\n\t\te.attrs = append(e.attrs, attribute.BoolSlice(k, v))\n\tcase float64:\n\t\te.attrs = append(e.attrs, attribute.Float64(k, v))\n\tcase []float64:\n\t\te.attrs = append(e.attrs, attribute.Float64Slice(k, v))\n\tcase int:\n\t\te.attrs = append(e.attrs, attribute.Int(k, v))\n\tcase []int:\n\t\te.attrs = append(e.attrs, attribute.IntSlice(k, v))\n\tcase int64:\n\t\te.attrs = append(e.attrs, attribute.Int64(k, v))\n\tcase []int64:\n\t\te.attrs = append(e.attrs, attribute.Int64Slice(k, v))\n\tcase string:\n\t\te.attrs = append(e.attrs, attribute.String(k, v))\n\tcase []string:\n\t\te.attrs = append(e.attrs, attribute.StringSlice(k, v))\n\tdefault:\n\t\tlog.Printf(\"bug: event.Add(): receiveing %T which is not supported\", v)\n\t}\n}\n\n// Done renders the Event to the span in the Context. If there are no attributes on the Event, this is a no-oop.\n// Once Done is called, the Event object MUST not be used again.\nfunc (e *Event) Done(ctx context.Context) {\n\tdefer pool.put(e)\n\n\tif e.name == \"\" {\n\t\treturn\n\t}\n\tspan := trace.SpanFromContext(ctx)\n\tif e.attrs == nil {\n\t\treturn\n\t}\n\tspan.AddEvent(e.name, trace.WithAttributes(e.attrs...))\n}\n\n// Println acts like log.Println() except we log to the OTEL span in the Context.\nfunc Println(ctx context.Context, v ...interface{}) {\n\tspan := trace.SpanFromContext(ctx)\n\tif !span.IsRecording() {\n\t\treturn\n\t}\n\tstd.output(span, 2, fmt.Sprintln(v...))\n}\n\n// Printf acts like log.Printf() except we log to the OTEL span in the Context.\nfunc Printf(ctx context.Context, format string, v ...interface{}) {\n\tspan := trace.SpanFromContext(ctx)\n\tif !span.IsRecording() {\n\t\treturn\n\t}\n\tstd.output(span, 2, fmt.Sprintf(format, v...))\n}\n\n// SetFlags sets the output flags for the standard logger.\nfunc SetFlags(flag int) {\n\tstd.flag = flag\n}\n\n// logger is an implementation of log.Logger that writes to a Span.\ntype logger struct {\n\tmu   sync.Mutex\n\tflag int    // properties\n\tbuf  []byte // for accumulating text to write\n}\n\n// Cheap integer to fixed-width decimal ASCII. Give a negative width to avoid zero-padding.\nfunc itoa(buf *[]byte, i int, wid int) {\n\t// Assemble decimal in reverse order.\n\tvar b [20]byte\n\tbp := len(b) - 1\n\tfor i >= 10 || wid > 1 {\n\t\twid--\n\t\tq := i / 10\n\t\tb[bp] = byte('0' + i - q*10)\n\t\tbp--\n\t\ti = q\n\t}\n\t// i < 10\n\tb[bp] = byte('0' + i)\n\t*buf = append(*buf, b[bp:]...)\n}\n\nfunc (l *logger) output(span trace.Span, calldepth int, s string) error {\n\tnow := time.Now() // get this early\n\tvar file string\n\tvar line int\n\tl.mu.Lock()\n\tdefer l.mu.Unlock()\n\n\tif l.flag&(Lshortfile|Llongfile) != 0 {\n\t\t// Release lock while getting caller info - it's expensive.\n\t\tl.mu.Unlock()\n\t\tvar ok bool\n\t\t_, file, line, ok = runtime.Caller(calldepth)\n\t\tif !ok {\n\t\t\tfile = \"???\"\n\t\t\tline = 0\n\t\t}\n\t\tl.mu.Lock()\n\t}\n\tl.buf = l.buf[:0]\n\tl.formatHeader(&l.buf, now, file, line)\n\tl.buf = append(l.buf, s...)\n\tif len(s) == 0 || s[len(s)-1] != '\\n' {\n\t\tl.buf = append(l.buf, '\\n')\n\t}\n\tspan.AddEvent(string(l.buf), trace.WithAttributes(attribute.Bool(\"log\", true)))\n\treturn nil\n}\n\n// formatHeader writes log header to buf in following order:\n//   * date and/or time (if corresponding flags are provided),\n//   * file and line number (if corresponding flags are provided),\nfunc (l *logger) formatHeader(buf *[]byte, t time.Time, file string, line int) {\n\tif l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {\n\t\tif l.flag&LUTC != 0 {\n\t\t\tt = t.UTC()\n\t\t}\n\t\tif l.flag&Ldate != 0 {\n\t\t\tyear, month, day := t.Date()\n\t\t\titoa(buf, year, 4)\n\t\t\t*buf = append(*buf, '/')\n\t\t\titoa(buf, int(month), 2)\n\t\t\t*buf = append(*buf, '/')\n\t\t\titoa(buf, day, 2)\n\t\t\t*buf = append(*buf, ' ')\n\t\t}\n\t\tif l.flag&(Ltime|Lmicroseconds) != 0 {\n\t\t\thour, min, sec := t.Clock()\n\t\t\titoa(buf, hour, 2)\n\t\t\t*buf = append(*buf, ':')\n\t\t\titoa(buf, min, 2)\n\t\t\t*buf = append(*buf, ':')\n\t\t\titoa(buf, sec, 2)\n\t\t\tif l.flag&Lmicroseconds != 0 {\n\t\t\t\t*buf = append(*buf, '.')\n\t\t\t\titoa(buf, t.Nanosecond()/1e3, 6)\n\t\t\t}\n\t\t\t*buf = append(*buf, ' ')\n\t\t}\n\t}\n\tif l.flag&(Lshortfile|Llongfile) != 0 {\n\t\tif l.flag&Lshortfile != 0 {\n\t\t\tshort := file\n\t\t\tfor i := len(file) - 1; i > 0; i-- {\n\t\t\t\tif file[i] == '/' {\n\t\t\t\t\tshort = file[i+1:]\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tfile = short\n\t\t}\n\t\t*buf = append(*buf, file...)\n\t\t*buf = append(*buf, ':')\n\t\titoa(buf, line, -1)\n\t\t*buf = append(*buf, \": \"...)\n\t}\n}\n"
  },
  {
    "path": "chapter/14/petstore-operator/client/internal/server/storage/mem/mem.go",
    "content": "// Package mem contains an in-memory storage implementation of storage.Data.\n// This is great for unit tests and demos. Our implementation uses a\n// left-leaning red black tree for storage of entries by birthdays and maps\n// for all other indexes. Filtering is done by searching all indexes for matches\n// by each filter and if all matches succeed we stream the entry found.\npackage mem\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/14/petstore-operator/client/internal/server/errors\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/14/petstore-operator/client/internal/server/log\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/14/petstore-operator/client/internal/server/storage\"\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/14/petstore-operator/client/proto\"\n\n\t\"github.com/biogo/store/llrb\"\n)\n\n// birthdays represents a set of pets that share the same birthday with\n// keys that are pet IDs. This is what we insert into our birthday tree.\ntype birthdays map[string]*pb.Pet\n\n// Compare implements the llrb.Comparable.Compare().\nfunc (bi birthdays) Compare(b llrb.Comparable) int {\n\tvar ap, bp *pb.Pet\n\t// Get any entry in the map, all have the same birthday.\n\tfor _, ap = range bi {\n\t\tbreak\n\t}\n\tfor _, bp = range b.(birthdays) {\n\t\tbreak\n\t}\n\n\t// Ignore errors because we have to conform to a function def\n\t// and we should not be storing records with errors in the Birthday.\n\tat, _ := storage.BirthdayToTime(nil, ap.Birthday)\n\tbt, _ := storage.BirthdayToTime(nil, bp.Birthday)\n\n\tswitch {\n\tcase at.Before(bt):\n\t\treturn -1\n\tcase at.Equal(bt):\n\t\treturn 0\n\t}\n\treturn 1\n}\n\n// birthdayGet is what we use to search for a pets with a particular birthday.\ntype birthdayGet struct {\n\t*pb.Pet\n}\n\n// Compare implements the llrb.Comparable.Compare().\nfunc (bi birthdayGet) Compare(b llrb.Comparable) int {\n\t// Ignore errors because we have to conform to a function def\n\t// and we should not be storing records with errors in the Birthday.\n\tat, _ := storage.BirthdayToTime(nil, bi.Pet.Birthday)\n\tvar bt time.Time\n\tswitch v := b.(type) {\n\tcase birthdayGet:\n\t\tbt, _ = storage.BirthdayToTime(nil, v.Pet.Birthday)\n\tcase birthdays:\n\t\tvar p *pb.Pet\n\t\tfor _, p = range v {\n\t\t\tbreak\n\t\t}\n\t\tbt, _ = storage.BirthdayToTime(nil, p.Birthday)\n\t}\n\n\tswitch {\n\tcase at.Before(bt):\n\t\treturn -1\n\tcase at.Equal(bt):\n\t\treturn 0\n\t}\n\treturn 1\n}\n\n// Data implements storage.Data.\ntype Data struct {\n\tmu       sync.RWMutex // protects the items in this block\n\tbirthday *llrb.Tree\n\tnames    map[string]map[string]*pb.Pet\n\tids      map[string]*pb.Pet\n\ttypes    map[pb.PetType]map[string]*pb.Pet\n\n\t// searches contains all the search calls that must be done\n\t// when we do a search. This is populated in New().\n\tsearches []func(context.Context, *pb.SearchPetsReq) []string\n}\n\n// New is the constructor for Data.\nfunc New() *Data {\n\td := Data{\n\t\tnames:    map[string]map[string]*pb.Pet{},\n\t\tids:      map[string]*pb.Pet{},\n\t\tbirthday: &llrb.Tree{},\n\t\ttypes:    map[pb.PetType]map[string]*pb.Pet{},\n\t}\n\td.searches = []func(context.Context, *pb.SearchPetsReq) []string{\n\t\td.byNames,\n\t\td.byTypes,\n\t\td.byBirthdays,\n\t}\n\treturn &d\n}\n\n// AddPets implements storage.Data.AddPets().\nfunc (d *Data) AddPets(ctx context.Context, pets []*pb.Pet) error {\n\te := log.NewEvent(\"mem.data.AddPets()\")\n\tdefer e.Done(ctx)\n\tstart := time.Now()\n\tdefer func() {\n\t\te.Add(\"latency.ns\", time.Since(start))\n\t}()\n\n\td.mu.RLock()\n\t// Make sure that none of these IDs somehow exist already.\n\tfor _, p := range pets {\n\t\tif _, ok := d.ids[p.Id]; ok {\n\t\t\treturn errors.Errorf(ctx, \"pet with ID(%s) is already present\", p.Id)\n\t\t}\n\t}\n\td.mu.RUnlock()\n\n\td.mu.Lock()\n\tdefer d.mu.Unlock()\n\n\td.populate(ctx, pets)\n\treturn nil\n}\n\n// UpdatePets implements storage.Data.AddPets().\nfunc (d *Data) UpdatePets(ctx context.Context, pets []*pb.Pet) error {\n\td.mu.RLock()\n\t// Make sure that ALL of these IDs somehow exist already.\n\tfor _, p := range pets {\n\t\tif _, ok := d.ids[p.Id]; !ok {\n\t\t\treturn errors.Errorf(ctx, \"pet with ID(%s) doesn't exist\", p.Id)\n\t\t}\n\t}\n\td.mu.RUnlock()\n\n\td.mu.Lock()\n\tdefer d.mu.Unlock()\n\n\td.populate(ctx, pets)\n\treturn nil\n}\n\nfunc (d *Data) populate(ctx context.Context, pets []*pb.Pet) {\n\te := log.NewEvent(\"mem.data.populate()\")\n\tdefer e.Done(ctx)\n\n\tfor _, p := range pets {\n\t\td.ids[p.Id] = p\n\t\tif v, ok := d.names[p.Name]; ok {\n\t\t\tv[p.Id] = p\n\t\t} else {\n\t\t\td.names[p.Name] = map[string]*pb.Pet{\n\t\t\t\tp.Id: p,\n\t\t\t}\n\t\t}\n\t\tif v, ok := d.types[p.Type]; ok {\n\t\t\tv[p.Id] = p\n\t\t} else {\n\t\t\td.types[p.Type] = map[string]*pb.Pet{\n\t\t\t\tp.Id: p,\n\t\t\t}\n\t\t}\n\t\tv := d.birthday.Get(birthdayGet{p})\n\t\tif v == nil {\n\t\t\td.birthday.Insert(birthdays{p.Id: p})\n\t\t\tcontinue\n\t\t}\n\t\tv.(birthdays)[p.Id] = p\n\t}\n}\n\n// DeletePets implements stroage.Data.DeletePets().\nfunc (d *Data) DeletePets(ctx context.Context, ids []string) error {\n\te := log.NewEvent(\"mem.data.DeletePets()\")\n\tdefer e.Done(ctx)\n\tstart := time.Now()\n\tdefer func() {\n\t\te.Add(\"latency.ns\", time.Since(start))\n\t}()\n\n\td.mu.Lock()\n\tdefer d.mu.Unlock()\n\n\tfor _, id := range ids {\n\t\tp, ok := d.ids[id]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tdelete(d.ids, id)\n\t\tif v, ok := d.names[p.Name]; ok {\n\t\t\tif len(v) == 1 {\n\t\t\t\tdelete(d.names, p.Name)\n\t\t\t} else {\n\t\t\t\tdelete(v, id)\n\t\t\t}\n\t\t}\n\t\tif v, ok := d.types[p.Type]; ok {\n\t\t\tif len(v) == 1 {\n\t\t\t\tdelete(d.types, p.Type)\n\t\t\t} else {\n\t\t\t\tdelete(v, id)\n\t\t\t}\n\t\t}\n\t\tv := d.birthday.Get(birthdayGet{p})\n\t\tif v == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif len(v.(birthdays)) == 1 {\n\t\t\td.birthday.Delete(birthdayGet{p})\n\t\t}\n\t\tdelete(v.(birthdays), p.Id)\n\t}\n\treturn nil\n}\n\n// SearchPets implements storage.Data.SearchPets().\nfunc (d *Data) SearchPets(ctx context.Context, filter *pb.SearchPetsReq) chan storage.SearchItem {\n\tpetsCh := make(chan storage.SearchItem, 1)\n\n\tgo func() {\n\t\tdefer close(petsCh)\n\t\td.searchPets(ctx, filter, petsCh)\n\t}()\n\n\treturn petsCh\n}\n\nfunc (d *Data) searchPets(ctx context.Context, filter *pb.SearchPetsReq, out chan storage.SearchItem) {\n\te := log.NewEvent(\"mem.data.searchPets()\")\n\tdefer e.Done(ctx)\n\n\td.mu.RLock()\n\tdefer d.mu.RUnlock()\n\n\tfilters := 0\n\tif len(filter.Names) > 0 {\n\t\te.Add(\"filterNames\", true)\n\t\tfilters++\n\t}\n\tif len(filter.Types) > 0 {\n\t\te.Add(\"filterTypes\", true)\n\t\tfilters++\n\t}\n\tif filter.BirthdateRange != nil {\n\t\te.Add(\"filterBirthday\", true)\n\t\tfilters++\n\t}\n\n\t// They didn't provide filters, so just return everything.\n\tif filters == 0 {\n\t\te.Add(\"returnAll\", true)\n\t\td.returnAll(ctx, out)\n\t\treturn\n\t}\n\n\tsearchCh := make(chan []string, len(d.searches))\n\twg := sync.WaitGroup{}\n\twg.Add(len(d.searches))\n\n\tgoCount := 0\n\t// Spin off our searches.\n\tfor _, search := range d.searches {\n\t\tgoCount++\n\t\tsearch := search\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tr := search(ctx, filter)\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\tcase searchCh <- r:\n\t\t\t}\n\t\t}()\n\t}\n\te.Add(\"search.goroutines\", goCount)\n\n\t// Wait for our searches to complete then close our searchCh.\n\tgo func() { wg.Wait(); close(searchCh) }()\n\n\t// Collect all IDs from searches and count them. When one hits\n\t// the total number of filters send the matching pet to the caller.\n\tm := map[string]int{}\n\tmatchCh := make(chan string, 1)\n\tgo func() {\n\t\tdefer close(matchCh)\n\t\tfor ids := range searchCh {\n\t\t\tfor _, id := range ids {\n\t\t\t\tcount := m[id]\n\t\t\t\tcount++\n\t\t\t\tm[id] = count\n\t\t\t\tif count == filters {\n\t\t\t\t\tmatchCh <- id\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n\n\t// This handles all our matches getting returned.\n\tvalCount := 0\n\tlatency := 0\n\tdefer func() {\n\t\tif valCount > 0 {\n\t\t\te.Add(\"upstream.recv.latency.avg.ns\", latency/valCount)\n\t\t}\n\t}()\n\n\tfor {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\tcase id, ok := <-matchCh:\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tstart := time.Now()\n\t\t\tout <- storage.SearchItem{Pet: d.ids[id]}\n\t\t\tvalCount++\n\t\t\tlatency += int(time.Since(start))\n\t\t}\n\t}\n}\n\n// returnAll streams all the pets that we have.\nfunc (d *Data) returnAll(ctx context.Context, out chan storage.SearchItem) {\n\te := log.NewEvent(\"mem.data.returnAll()\")\n\tdefer e.Done(ctx)\n\n\tstart := time.Now()\n\tcount := 0\n\tdefer func() {\n\t\te.Add(\"latency.ns\", int(time.Since(start)))\n\t\te.Add(\"count\", count)\n\t}()\n\n\tfor _, p := range d.ids {\n\t\tcount++\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\tcase out <- storage.SearchItem{Pet: p}:\n\t\t}\n\t}\n}\n\n// byNames returns IDs of pets that have the names matched in the filter.\nfunc (d *Data) byNames(ctx context.Context, filter *pb.SearchPetsReq) []string {\n\tif len(filter.Names) == 0 {\n\t\treturn nil\n\t}\n\n\te := log.NewEvent(\"mem.data.byNames()\")\n\tdefer e.Done(ctx)\n\n\tstart := time.Now()\n\tcount := 0\n\tdefer func() {\n\t\te.Add(\"latency.ns\", int(time.Since(start)))\n\t\te.Add(\"count\", count)\n\t}()\n\tvar ids []string\n\tfor _, n := range filter.Names {\n\t\tcount++\n\t\tif ctx.Err() != nil {\n\t\t\treturn nil\n\t\t}\n\t\tp, ok := d.names[n]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tfor id := range p {\n\t\t\tids = append(ids, id)\n\t\t}\n\t}\n\treturn ids\n}\n\n// byTypes returns IDs of pets that have the types matched in the filter.\nfunc (d *Data) byTypes(ctx context.Context, filter *pb.SearchPetsReq) []string {\n\tif len(filter.Types) == 0 {\n\t\treturn nil\n\t}\n\te := log.NewEvent(\"mem.data.byTypes()\")\n\tdefer e.Done(ctx)\n\n\tstart := time.Now()\n\tcount := 0\n\tdefer func() {\n\t\te.Add(\"latency.ns\", int(time.Since(start)))\n\t\te.Add(\"count\", count)\n\t}()\n\tvar ids []string\n\tfor _, t := range filter.Types {\n\t\tcount++\n\t\tif ctx.Err() != nil {\n\t\t\treturn nil\n\t\t}\n\t\tp, ok := d.types[t]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tfor id := range p {\n\t\t\tids = append(ids, id)\n\t\t}\n\t}\n\treturn ids\n}\n\n// byBirthdays returns IDs of pets that have the birthdays matched in the filter.\nfunc (d *Data) byBirthdays(ctx context.Context, filter *pb.SearchPetsReq) []string {\n\tif filter.BirthdateRange == nil {\n\t\treturn nil\n\t}\n\n\te := log.NewEvent(\"mem.data.byBirthdays()\")\n\tdefer e.Done(ctx)\n\n\tstart := time.Now()\n\tcount := 0\n\tdefer func() {\n\t\te.Add(\"latency.ns\", int(time.Since(start)))\n\t\te.Add(\"count\", count)\n\t}()\n\n\tvar ids []string\n\td.birthday.DoRange(\n\t\tfunc(c llrb.Comparable) (done bool) {\n\t\t\tfor _, p := range c.(birthdays) {\n\t\t\t\tif ctx.Err() != nil {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tids = append(ids, p.Id)\n\t\t\t}\n\t\t\treturn\n\t\t},\n\t\tbirthdayGet{&pb.Pet{Birthday: filter.BirthdateRange.Start}},\n\t\tbirthdayGet{&pb.Pet{Birthday: filter.BirthdateRange.End}},\n\t)\n\tif ctx.Err() != nil {\n\t\treturn nil\n\t}\n\tcount = len(ids)\n\treturn ids\n}\n"
  },
  {
    "path": "chapter/14/petstore-operator/client/internal/server/storage/mem/mem_test.go",
    "content": "package mem\n\nimport (\n\t\"context\"\n\t\"sort\"\n\t\"strconv\"\n\t\"testing\"\n\n\t\"github.com/kylelemons/godebug/pretty\"\n\tdpb \"google.golang.org/genproto/googleapis/type/date\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/14/petstore-operator/client/internal/server/storage\"\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/14/petstore-operator/client/proto\"\n)\n\n// This tests we implement the interface.\nvar _ storage.Data = &Data{}\n\nvar pets = []*pb.Pet{\n\t{\n\t\tId:       \"0\",\n\t\tName:     \"Adam\",\n\t\tType:     pb.PetType_PTCanine,\n\t\tBirthday: &dpb.Date{Month: 1, Day: 1, Year: 2020},\n\t},\n\t{\n\t\tId:       \"1\",\n\t\tName:     \"Becky\",\n\t\tType:     pb.PetType_PTFeline,\n\t\tBirthday: &dpb.Date{Month: 2, Day: 1, Year: 2020},\n\t},\n\t{\n\t\tId:       \"2\",\n\t\tName:     \"Calvin\",\n\t\tType:     pb.PetType_PTFeline,\n\t\tBirthday: &dpb.Date{Month: 2, Day: 2, Year: 2020},\n\t},\n\t{\n\t\tId:       \"3\",\n\t\tName:     \"David\",\n\t\tType:     pb.PetType_PTBird,\n\t\tBirthday: &dpb.Date{Month: 2, Day: 2, Year: 2021},\n\t},\n\t{\n\t\tId:       \"4\",\n\t\tName:     \"Elaine\",\n\t\tType:     pb.PetType_PTReptile,\n\t\tBirthday: &dpb.Date{Month: 2, Day: 2, Year: 2021},\n\t},\n\t{\n\t\tId:       \"5\",\n\t\tName:     \"Elaine\",\n\t\tType:     pb.PetType_PTReptile,\n\t\tBirthday: &dpb.Date{Month: 2, Day: 3, Year: 2021},\n\t},\n}\n\n// makePets takes the global \"pets\" var and clones everything in it and puts it into\n// a *Data so we have test data.\nfunc makePets() *Data {\n\td := New()\n\n\tn := []*pb.Pet{}\n\tfor _, p := range pets {\n\t\tn = append(n, proto.Clone(p).(*pb.Pet))\n\t}\n\n\td.AddPets(context.Background(), n)\n\treturn d\n}\n\nfunc TestByNames(t *testing.T) {\n\td := makePets()\n\n\tgot := d.byNames(context.Background(), &pb.SearchPetsReq{Names: []string{\"David\", \"Elaine\"}})\n\tsort.Strings(got)\n\n\twant := []string{\"3\", \"4\", \"5\"}\n\tif diff := pretty.Compare(want, got); diff != \"\" {\n\t\tt.Errorf(\"TestByNames: -want/+got:\\n%s\", diff)\n\t}\n}\n\nfunc TestByTypes(t *testing.T) {\n\td := makePets()\n\n\tgot := d.byTypes(context.Background(), &pb.SearchPetsReq{Types: []pb.PetType{pb.PetType_PTCanine, pb.PetType_PTReptile}})\n\tsort.Strings(got)\n\n\twant := []string{\"0\", \"4\", \"5\"}\n\tif diff := pretty.Compare(want, got); diff != \"\" {\n\t\tt.Errorf(\"TestByTypes: -want/+got:\\n%s\", diff)\n\t}\n}\n\nfunc TestByBirthdays(t *testing.T) {\n\td := makePets()\n\n\tgot := d.byBirthdays(\n\t\tcontext.Background(),\n\t\t&pb.SearchPetsReq{\n\t\t\tBirthdateRange: &pb.DateRange{\n\t\t\t\tStart: &dpb.Date{Month: 2, Day: 1, Year: 2020},\n\t\t\t\tEnd:   &dpb.Date{Month: 2, Day: 3, Year: 2021},\n\t\t\t},\n\t\t},\n\t)\n\tsort.Strings(got)\n\n\twant := []string{\"1\", \"2\", \"3\", \"4\"}\n\tif diff := pretty.Compare(want, got); diff != \"\" {\n\t\tt.Errorf(\"TestByBirthdays: -want/+got:\\n%s\", diff)\n\t}\n}\n\nfunc TestDeletePets(t *testing.T) {\n\td := makePets()\n\n\tdeletions := []string{\"3\", \"5\", \"20\"}\n\n\tif err := d.DeletePets(context.Background(), deletions); err != nil {\n\t\tt.Fatalf(\"TestDeletePets: got err == %v, want err == nil\", err)\n\t}\n\n\t// Don't check the last deletion, it is only there to make sure\n\t// a non-existent value doesn't do anything.\n\tfor _, id := range deletions[:len(deletions)-1] {\n\t\tif _, ok := d.ids[id]; ok {\n\t\t\tt.Errorf(\"TestDeletePets: found ids[%s]\", id)\n\t\t}\n\t\ti, _ := strconv.Atoi(id)\n\n\t\tif m, ok := d.names[pets[i].Name]; ok {\n\t\t\tif _, ok := m[id]; ok {\n\t\t\t\tt.Errorf(\"TestDeletePets: found(%s) in names\", id)\n\t\t\t}\n\t\t}\n\t\tif m, ok := d.types[pets[i].Type]; ok {\n\t\t\tif _, ok := m[id]; ok {\n\t\t\t\tt.Errorf(\"TestDeletePets: found(%s) in types\", id)\n\t\t\t}\n\t\t}\n\n\t\tv := d.birthday.Get(birthdayGet{pets[i]})\n\t\tif v != nil {\n\t\t\tif _, ok := v.(birthdays)[id]; ok {\n\t\t\t\tt.Errorf(\"TestDeletePets: found(%s) in birthday tree\", id)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestSearchPets(t *testing.T) {\n\td := makePets()\n\n\tch := d.SearchPets(\n\t\tcontext.Background(),\n\t\t&pb.SearchPetsReq{\n\t\t\tNames: []string{\n\t\t\t\t\"Becky\",\n\t\t\t\t\"Calvin\",\n\t\t\t\t\"David\",\n\t\t\t\t\"Elaine\",\n\t\t\t},\n\t\t\tTypes: []pb.PetType{\n\t\t\t\tpb.PetType_PTReptile,\n\t\t\t\tpb.PetType_PTFeline,\n\t\t\t},\n\t\t\tBirthdateRange: &pb.DateRange{\n\t\t\t\tStart: &dpb.Date{Month: 2, Day: 2, Year: 2021},\n\t\t\t\tEnd:   &dpb.Date{Month: 2, Day: 3, Year: 2021},\n\t\t\t},\n\t\t},\n\t)\n\n\tgot := []storage.SearchItem{}\n\tfor item := range ch {\n\t\tgot = append(got, item)\n\t}\n\n\twant := []storage.SearchItem{{Pet: pets[4]}}\n\n\tconfig := pretty.Config{TrackCycles: true}\n\tif diff := config.Compare(want, got); diff != \"\" {\n\t\tt.Errorf(\"TestSearchPets: -want/+got:\\n%s\", diff)\n\t}\n}\n"
  },
  {
    "path": "chapter/14/petstore-operator/client/internal/server/storage/storage.go",
    "content": "package storage\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"time\"\n\n\tdpb \"google.golang.org/genproto/googleapis/type/date\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/14/petstore-operator/client/internal/server/errors\"\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/14/petstore-operator/client/proto\"\n)\n\n// Data represents our data storage.\ntype Data interface {\n\t// AddPets adds pet entries into storage.\n\tAddPets(ctx context.Context, pets []*pb.Pet) error\n\t// UpdatePets updates pet entries in storage.\n\tUpdatePets(ctx context.Context, pets []*pb.Pet) error\n\t// DeletePets deletes pets in storage by their ID. Will not error\n\t// on IDs not found.\n\tDeletePets(ctx context.Context, ids []string) error\n\t// SearchPets searches storage for pet entries that match the\n\t// filter.\n\tSearchPets(ctx context.Context, filter *pb.SearchPetsReq) chan SearchItem\n}\n\n// SearchItem is an item returned by a search.\ntype SearchItem struct {\n\t// Pet is the pet that matched the search filters.\n\tPet *pb.Pet\n\t// Error indicates that there was an error. If set the channel\n\t// will close after this entry.\n\tError error\n}\n\n// ValidatePet validates that *pb.Pet has valid fields.\nfunc ValidatePet(ctx context.Context, p *pb.Pet, forUpdate bool) error {\n\tif forUpdate && p.Id == \"\" {\n\t\treturn errors.New(ctx, \"updates must have the Id field set\")\n\t} else {\n\t\tif !forUpdate && p.Id != \"\" {\n\t\t\treturn errors.New(ctx, \"cannot set the Id field\")\n\t\t}\n\t}\n\tp.Name = strings.TrimSpace(p.Name)\n\tif p.Name == \"\" {\n\t\treturn errors.New(ctx, \"cannot have a pet without a name\")\n\t}\n\n\tif p.Type == pb.PetType_PTUnknown {\n\t\treturn errors.New(ctx, \"cannot have an unknown pet type\")\n\t}\n\n\t_, err := BirthdayToTime(ctx, p.Birthday)\n\tif err != nil {\n\t\treturn errors.Errorf(ctx, \"pet(%s) had an error in its birthday: %w\", p.Name, err)\n\t}\n\treturn nil\n\n}\n\n// BirthdayToTime converts the *pb.Pet.Birthday field to a time.Time object.\nfunc BirthdayToTime(ctx context.Context, d *dpb.Date) (time.Time, error) {\n\tif d.Month < 1 || d.Month > 12 {\n\t\treturn time.Time{}, errors.Errorf(ctx, \"month must be 1-12, was %d\", d.Month)\n\t}\n\tif d.Day < 1 || d.Day > 31 {\n\t\treturn time.Time{}, errors.Errorf(ctx, \"day(%d) was invalid\", d.Day)\n\t}\n\tt := time.Date(int(d.Year), time.Month(d.Month), int(d.Day), 0, 0, 0, 0, time.UTC)\n\n\tif t.Month() != time.Month(d.Month) {\n\t\treturn time.Time{}, errors.Errorf(ctx, \"month %v does not have %d days\", time.Month(d.Month), d.Day)\n\t}\n\treturn t, nil\n}\n"
  },
  {
    "path": "chapter/14/petstore-operator/client/internal/server/telemetry/metrics/metrics.go",
    "content": "/*\nPackage metrics provides setup of metrics that can be used internally to measure various application states.\nAll metrics for the application are defined here and other applications use this package to grab the metrics\nand use them. This package will also report any metric that is not used in the first 10 seconds after the app has started\nto prevent useless metrics from existing, as all metrics should be grabbed by that time.\n\nIn a package you want to set metrics, you can do it as follows:\n\tvar addCount metrics.Int64Counter\n\n\tfunc init() {\n\t\taddCounter = metrics.Get.Int64(\"petstore/server/AddPets/requests\")\n\t}\n\t...\n\n\tfunc (s *Server) AddPets(ctx context.Context, req *pb.AddPetsReq) (*pb.AddpetsResp, error) {\n\t\t...\n\t\t// Do this if you have multiple changes that don't require special labels per update.\n\t\tmetrics.Meter.RecordBatch(ctx, nil, addCounter.Measure(ctx, 1))\n\t\t// Do this if you only need to make one change or need special labels.\n\t\taddCounter.Add(ctx, 1, attribute.String(\"label\", \"value\")\n\t\t...\n\t}\n\n\nTo cause metrics to be exported package main():\n\tfunc main() {\n\t\t...\n\t\tstop, err := metrics.Start(ctx, metrics.OTELGRPC{Addr: \"ip:port\"})\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\tdefer stop()\n\t\t...\n\t}\n*/\npackage metrics\n\nimport (\n\t\"html/template\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/14/petstore-operator/client/internal/server/log\"\n\n\t\"go.opentelemetry.io/otel/metric\"\n\t\"go.opentelemetry.io/otel/metric/global\"\n)\n\ntype metricType int\n\nconst (\n\tunknown     = 0\n\tmtInt64     = 1\n\tmtInt64Hist = 2\n\tmtInt64UD   = 3\n)\n\ntype metricDef struct {\n\tmtype metricType\n\tname  string\n\tdesc  string\n}\n\nvar metrics = []metricDef{\n\t// Histograms\n\t{mtInt64Hist, \"petstore/server/AddPets/latency\", \"The latency of an AddPets() request in nanoseconds\"},\n\t{mtInt64Hist, \"petstore/server/DeletePets/latency\", \"The latency of an DeletePets() request in nanoseconds\"},\n\t{mtInt64Hist, \"petstore/server/UpdatePets/latency\", \"The latency of an UpdatePets() request in nanoseconds\"},\n\t{mtInt64Hist, \"petstore/server/SearchPets/latency\", \"The latency of a SearchPets() request in nanoseconds\"},\n\t// Counters\n\t{mtInt64, \"petstore/server/AddPets/requests\", \"The total requests made to AddPets()\"},\n\t{mtInt64, \"petstore/server/DeletePets/requests\", \"The total requests made to DeletePets()\"},\n\t{mtInt64, \"petstore/server/UpdatePets/requests\", \"The total requests made to UpdatePets()\"},\n\t{mtInt64, \"petstore/server/SearchPets/requests\", \"The total requests made to SearchPets()\"},\n\t{mtInt64, \"petstore/server/totals/requests\", \"The total requests made to the server\"},\n\n\t{mtInt64, \"petstore/server/AddPets/errors\", \"The total error count\"},\n\t{mtInt64, \"petstore/server/DeletePets/errors\", \"The total error couunt\"},\n\t{mtInt64, \"petstore/server/UpdatePets/errors\", \"The total error count\"},\n\t{mtInt64, \"petstore/server/SearchPets/errors\", \"The total error count\"},\n\t{mtInt64, \"petstore/server/totals/errors\", \"The total error count for all RPCs\"},\n\n\t// UpDown Counters\n\t{mtInt64UD, \"petstore/server/AddPets/current\", \"The amount of requests currently being proccessed\"},\n\t{mtInt64UD, \"petstore/server/DeletePets/current\", \"The amount of requests currently being proccessed\"},\n\t{mtInt64UD, \"petstore/server/UpdatePets/current\", \"The amount of requests currently being proccessed\"},\n\t{mtInt64UD, \"petstore/server/SearchPets/current\", \"The amount of requests currently being proccessed\"},\n}\n\n// Meter is the meter for the petstore.\nvar Meter = global.Meter(\"petstore\")\n\n// Get is used to lookup metrics by name.\nvar Get = newLookups()\n\nvar unusedMetricsTmpl = template.Must(\n\ttemplate.New(\"\").Parse(\n\t\t`\nThe following metrics appeart to be unused:\n{{- range .}}\n\t{{.}}\n{{- end }}\n`,\n\t),\n)\n\n// Lookups provides lookups for metrics based on their names.\ntype Lookups struct {\n\tmtInt64Hist map[string]metric.Int64Histogram\n\tmtInt64UD   map[string]metric.Int64UpDownCounter\n\tmtInt64     map[string]metric.Int64Counter\n\n\tmu   sync.Mutex\n\tused map[string]bool\n}\n\nfunc newLookups() *Lookups {\n\tl := &Lookups{\n\t\tmtInt64Hist: map[string]metric.Int64Histogram{},\n\t\tmtInt64:     map[string]metric.Int64Counter{},\n\t\tmtInt64UD:   map[string]metric.Int64UpDownCounter{},\n\t\tused:        map[string]bool{},\n\t}\n\n\texists := map[string]bool{}\n\tfor _, m := range metrics {\n\t\tif m.mtype == unknown {\n\t\t\tlog.Logger.Fatalf(\"metric with type(%v) cannot be added\", m.mtype)\n\t\t}\n\t\tif m.name == \"\" {\n\t\t\tlog.Logger.Fatalf(\"metric cannot be missing a name\")\n\t\t}\n\t\tif m.desc == \"\" {\n\t\t\tlog.Logger.Fatalf(\"metric cannot be missing a desc\")\n\t\t}\n\t\tif exists[m.name] {\n\t\t\tlog.Logger.Fatalf(\"cannot have two metrics with same name(%s)\", m.name)\n\t\t}\n\t\texists[m.name] = true\n\n\t\tswitch m.mtype {\n\t\tcase mtInt64Hist:\n\t\t\tl.mtInt64Hist[m.name] = metric.Must(Meter).NewInt64Histogram(m.name, metric.WithDescription(m.desc))\n\t\tcase mtInt64UD:\n\t\t\tl.mtInt64UD[m.name] = metric.Must(Meter).NewInt64UpDownCounter(m.name, metric.WithDescription(m.desc))\n\t\tcase mtInt64:\n\t\t\tl.mtInt64[m.name] = metric.Must(Meter).NewInt64Counter(m.name, metric.WithDescription(m.desc))\n\t\tdefault:\n\t\t\tlog.Logger.Fatalf(\"bug: we defined a metric type(%v) without adding support\", m.mtype)\n\t\t}\n\t}\n\tgo func() {\n\t\ttime.Sleep(10 * time.Second)\n\t\tunused := l.unused()\n\t\ts := strings.Builder{}\n\t\tif err := unusedMetricsTmpl.Execute(&s, unused); err != nil {\n\t\t\tlog.Logger.Fatalf(\"unusedMetricTmpl execute error: %s\", err)\n\t\t}\n\t\tlog.Logger.Println(s.String())\n\t}()\n\treturn l\n}\n\n// Int64 grabs the Int64Counter metric named \"s\". If not found, panics.\nfunc (l *Lookups) Int64(s string) metric.Int64Counter {\n\tl.mu.Lock()\n\tdefer l.mu.Unlock()\n\n\tm, ok := l.mtInt64[s]\n\tif !ok {\n\t\tlog.Logger.Fatalf(\"int64 metric(%s) is not defined\", s)\n\t}\n\tl.used[s] = true\n\treturn m\n}\n\n// Int64s grabs a list of Int64Counters.\nfunc (l *Lookups) Int64s(s ...string) []metric.Int64Counter {\n\tv := make([]metric.Int64Counter, 0, len(s))\n\tfor _, name := range s {\n\t\tv = append(v, l.Int64(name))\n\t}\n\treturn v\n}\n\n// Int64UD grabs the Int64UpDownCounter metric named \"s\". If not found, panics.\nfunc (l *Lookups) Int64UD(s string) metric.Int64UpDownCounter {\n\tl.mu.Lock()\n\tdefer l.mu.Unlock()\n\n\tm, ok := l.mtInt64UD[s]\n\tif !ok {\n\t\tlog.Logger.Fatalf(\"int64ud metric(%s) is not defined\", s)\n\t}\n\tl.used[s] = true\n\treturn m\n}\n\n// Int64UDs grabs a list of Int64UpDownCounters.\nfunc (l *Lookups) Int64UDs(s ...string) []metric.Int64UpDownCounter {\n\tv := make([]metric.Int64UpDownCounter, 0, len(s))\n\tfor _, name := range s {\n\t\tv = append(v, l.Int64UD(name))\n\t}\n\treturn v\n}\n\n// Int64Hist grabs the Int64Histogram metric named \"s\". If not found, panics.\nfunc (l *Lookups) Int64Hist(s string) metric.Int64Histogram {\n\tl.mu.Lock()\n\tdefer l.mu.Unlock()\n\n\tm, ok := l.mtInt64Hist[s]\n\tif !ok {\n\t\tlog.Logger.Fatalf(\"int64 histogram metric(%s) is not defined\", s)\n\t}\n\tl.used[s] = true\n\treturn m\n}\n\nfunc (l *Lookups) Int64Hists(s ...string) []metric.Int64Histogram {\n\tv := make([]metric.Int64Histogram, 0, len(s))\n\tfor _, name := range s {\n\t\tv = append(v, l.Int64Hist(name))\n\t}\n\treturn v\n}\n\nfunc (l *Lookups) unused() []string {\n\tl.mu.Lock()\n\tdefer l.mu.Unlock()\n\n\tunused := []string{}\n\tfor k := range l.mtInt64Hist {\n\t\tif !l.used[k] {\n\t\t\tunused = append(unused, k)\n\t\t}\n\t}\n\tfor k := range l.mtInt64 {\n\t\tif !l.used[k] {\n\t\t\tunused = append(unused, k)\n\t\t}\n\t}\n\tsort.Strings(unused)\n\treturn unused\n}\n"
  },
  {
    "path": "chapter/14/petstore-operator/client/internal/server/telemetry/metrics/start.go",
    "content": "package metrics\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlpmetric\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc\"\n\t\"go.opentelemetry.io/otel/metric/global\"\n\t\"go.opentelemetry.io/otel/sdk/metric/controller/basic\"\n\tprocessor \"go.opentelemetry.io/otel/sdk/metric/processor/basic\"\n\t\"go.opentelemetry.io/otel/sdk/metric/selector/simple\"\n)\n\n// Controller represents the controller to send metrics to.\ntype Controller interface {\n\tisController()\n}\n\n// OTELGRPC represents exporting to the \"go.opentelemetry.io/otel/sdk/metric/controller/basic\" controller.\ntype OTELGRPC struct {\n\t// Addr is the local address to export on.\n\tAddr string\n}\n\nfunc (o OTELGRPC) isController() {}\n\n// Stop is used to stop OTEL metric handling.\ntype Stop func()\n\n// Start is used to start OTEL metric handling.\nfunc Start(ctx context.Context, c Controller) (Stop, error) {\n\tcontrol, err := newController(ctx, c)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\terr = control.Start(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn func() {\n\t\tctx, cancel := context.WithTimeout(ctx, 1*time.Second)\n\t\tdefer cancel()\n\t\tif err := control.Stop(ctx); err != nil {\n\t\t\totel.Handle(err)\n\t\t}\n\t}, nil\n}\n\nfunc newController(ctx context.Context, c Controller) (*basic.Controller, error) {\n\tswitch v := c.(type) {\n\tcase OTELGRPC:\n\t\treturn otelGRPC(ctx, v)\n\t}\n\treturn nil, fmt.Errorf(\"%T is not a valid Controller\", c)\n}\n\nfunc otelGRPC(ctx context.Context, args OTELGRPC) (*basic.Controller, error) {\n\tmetricClient := otlpmetricgrpc.NewClient(\n\t\totlpmetricgrpc.WithInsecure(),\n\t\totlpmetricgrpc.WithEndpoint(args.Addr),\n\t)\n\tmetricExp, err := otlpmetric.New(ctx, metricClient)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"Failed to create the collector metric exporter\")\n\t}\n\n\tpusher := basic.New(\n\t\tprocessor.NewFactory(\n\t\t\tsimple.NewWithHistogramDistribution(),\n\t\t\tmetricExp,\n\t\t),\n\t\tbasic.WithExporter(metricExp),\n\t\tbasic.WithCollectPeriod(10*time.Second),\n\t)\n\tglobal.SetMeterProvider(pusher)\n\treturn pusher, nil\n}\n"
  },
  {
    "path": "chapter/14/petstore-operator/client/internal/server/telemetry/tracing/sampler/sampler.go",
    "content": "/*\nPackage sampler offers a Sampler that looks for a TraceID.Valid() == true or a gRPC metadata key called \"trace\"\nand if they exist will sample. Otherwise it looks to a child Sampler to determine based upon whatever sampler\nalgorithm is used.\n\nIn addition we offer the ability to switch out the underlying sampler at anytime in a thread-safe way.\n\nYou can construct a new Sampler like so:\n\ts, err := New(trace.NeverSample)\n\tif err != nil {\n\t\t// Do something\n\t}\n\nThe above Sampler would only trace if a TraceID.Valid() == true or gRCP metadate key called \"trace\" existed.\n\nIf we want to trace 1% of the time as well, we can do the following:\n\ts, err := New(trace.TraceIDRatioBased(.01))\n\tif err != nil {\n\t\t// Do something\n\t}\n*/\npackage sampler\n\nimport (\n\t\"fmt\"\n\t\"sync/atomic\"\n\n\t\"go.opentelemetry.io/otel/sdk/trace\"\n\totelTrace \"go.opentelemetry.io/otel/trace\"\n\t\"google.golang.org/grpc/metadata\"\n)\n\nconst desc = `This sampler samples if TracdID.Valid(), gRPC metadata contains key \"trace\" or the child sampler decides to sample`\n\n// Sampler decides whether a trace should be sampled and exported. This sampler will sample if\n// paramters TraceID.Valid() == true or the Context contains gRPC metadata that has key \"trace\" (it doesn't care about values).\ntype Sampler struct {\n\t// child stores a *trace.Sampler. trace.Sampler is an interface. Because atomic.Value cares about\n\t// the underlying type, you can't just store trace.Sampler. So we do a pointer, which is the only valid\n\t// use of *interface I've ever seen.\n\tchild atomic.Value // *trace.Sampler\n}\n\n// New creates a new Sampler with the child Sampler used if TraceID.Valid() == false and gRPC metadata does not contain\n// key \"trace\".\nfunc New(child trace.Sampler) (*Sampler, error) {\n\tif child == nil {\n\t\treturn nil, fmt.Errorf(\"child cannot == nil\")\n\t}\n\n\ts := &Sampler{}\n\ts.child.Store(&child)\n\treturn s, nil\n}\n\n// ShouldSample implements trace.Sampler.ShouldSample.\nfunc (s *Sampler) ShouldSample(p trace.SamplingParameters) trace.SamplingResult {\n\tpsc := otelTrace.SpanContextFromContext(p.ParentContext)\n\tif psc.IsValid() {\n\t\tif psc.IsRemote() {\n\t\t\tif psc.IsSampled() {\n\t\t\t\treturn trace.SamplingResult{\n\t\t\t\t\tDecision:   trace.RecordAndSample,\n\t\t\t\t\tTracestate: psc.TraceState(),\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif psc.IsSampled() {\n\t\t\treturn trace.SamplingResult{\n\t\t\t\tDecision:   trace.RecordAndSample,\n\t\t\t\tTracestate: psc.TraceState(),\n\t\t\t}\n\t\t}\n\t}\n\tmd, ok := metadata.FromIncomingContext(p.ParentContext)\n\tif !ok {\n\t\treturn (*s.child.Load().(*trace.Sampler)).ShouldSample(p)\n\t}\n\n\tif _, ok := md[\"trace\"]; ok {\n\t\tpsc := otelTrace.SpanContextFromContext(p.ParentContext)\n\t\treturn trace.SamplingResult{\n\t\t\tDecision:   trace.RecordAndSample,\n\t\t\tTracestate: psc.TraceState(),\n\t\t}\n\t}\n\n\treturn (*s.child.Load().(*trace.Sampler)).ShouldSample(p)\n}\n\n// Description implements trace.Sampler.Description().\nfunc (s *Sampler) Description() string {\n\treturn desc\n}\n\n// Switch switches the underlying trace.Sampler.\nfunc (s *Sampler) Switch(sampler trace.Sampler) {\n\tif sampler == nil {\n\t\tpanic(\"cannot call Switch() with a nil Sampler\")\n\t}\n\ts.child.Store(&sampler)\n}\n"
  },
  {
    "path": "chapter/14/petstore-operator/client/internal/server/telemetry/tracing/tracing.go",
    "content": "/*\nPackage tracing provides functions for starting and stopping our Open Telemetry tracing.\nThis package is intended to be used from main and is simple to use. We offer a few\nchoices on where traces export to. Here is an example to trace to stderr for all requests:\n\tfunc main() {\n\t\tctx := context.Background()\n\t\t// Set us up to always sample. The \"trace\" package is: \"petstore/server/SearchPets/latency\"\n\t\ttracing.Sampler.Switch(trace.AlwaysSample())\n\t\t// Start our tracing and pass the empty Stderr tracing arguments.\n\t\t// Stderr{} has no required fields.\n\t\tstop, err := tracing.Start(ctx, tracing.Stderr{})\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"problem starting telemetry: %s\", err)\n\t\t}\n\n\t\t// Stop kills our exporter when main() ends.\n\t\tdefer stop()\n\t}\n*/\npackage tracing\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc\"\n\t\"go.opentelemetry.io/otel/exporters/stdout/stdouttrace\"\n\t\"go.opentelemetry.io/otel/propagation\"\n\t\"go.opentelemetry.io/otel/sdk/resource\"\n\tsdktrace \"go.opentelemetry.io/otel/sdk/trace\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.4.0\"\n\t\"go.opentelemetry.io/otel/trace\"\n\t\"google.golang.org/grpc\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/14/petstore-operator/client/internal/server/telemetry/tracing/sampler\"\n)\n\n// Tracer is the tracer initialized by Start().\nvar (\n\t// Tracer is the tracer initialized by Start().\n\tTracer trace.Tracer // *sdktrace.TracerProvider //otlptrace.Exporter\n\t// Sampler is our *sampler.Sampler used by the Tracer.\n\tSampler *sampler.Sampler\n)\n\nfunc init() {\n\ts, err := sampler.New(sdktrace.TraceIDRatioBased(1))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tSampler = s\n}\n\n// Exporter represents the exporter to send telemetry to.\ntype Exporter interface {\n\tisExporter()\n}\n\n// OTELGRPC represents exporting to the go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc exporter.\ntype OTELGRPC struct {\n\t// Addr is the local address to export on.\n\tAddr string\n}\n\nfunc (o OTELGRPC) isExporter() {}\n\n// Stderr exports trace data to os.Stderr.\ntype Stderr struct{}\n\nfunc (s Stderr) isExporter() {}\n\n// File exports trace data to a file. If the file exists, it is overwritten.\ntype File struct {\n\t// Path is the path to the file.\n\tPath string\n}\n\nfunc (f File) isExporter() {}\n\n// Stop stops our Open Telemetry exporter.\ntype Stop func()\n\n// Start creates the OTEL exporter and configures the trace providers.\n// It returns a Stop() which will stop the exporter.\nfunc Start(ctx context.Context, e Exporter) (Stop, error) {\n\tlog.Println(\"Sampler: \", Sampler)\n\ttp, err := newTraceExporter(ctx, e)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tTracer = tp.Tracer(\"petstore\")\n\n\treturn func() {\n\t\tvar cancel context.CancelFunc\n\t\tctx, cancel = context.WithTimeout(ctx, 1*time.Second)\n\t\tdefer cancel()\n\n\t\tif err := tp.Shutdown(ctx); err != nil {\n\t\t\totel.Handle(err)\n\t\t}\n\t}, nil\n}\n\n// newTracerExporter creates an OTLP exporter with our tracer information.\nfunc newTraceExporter(ctx context.Context, e Exporter) (*sdktrace.TracerProvider, error) {\n\tvar exp sdktrace.SpanExporter\n\tvar err error\n\tswitch v := e.(type) {\n\tcase OTELGRPC:\n\t\texp, err = otelGRPC(ctx, v)\n\tcase Stderr:\n\t\texp, err = newFileExporter(os.Stderr)\n\tcase File:\n\t\tf, err := os.Create(v.Path)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\texp, err = newFileExporter(f)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"%T is not a valid Exporter\", e)\n\t}\n\n\tres, err := resource.New(\n\t\tctx,\n\t\tresource.WithFromEnv(),\n\t\tresource.WithProcess(),\n\t\tresource.WithTelemetrySDK(),\n\t\tresource.WithHost(),\n\t\tresource.WithAttributes(\n\t\t\t// the service name used to display traces in backends\n\t\t\tsemconv.ServiceNameKey.String(\"petstore\"),\n\t\t),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// set global propagator to tracecontext (the default is no-op).\n\totel.SetTextMapPropagator(propagation.TraceContext{})\n\n\tprov := sdktrace.NewTracerProvider(\n\t\tsdktrace.WithSampler(Sampler),\n\t\tsdktrace.WithBatcher(exp),\n\t\tsdktrace.WithResource(res),\n\t)\n\totel.SetTracerProvider(prov)\n\treturn prov, nil\n}\n\n// newFileExporter creates an exporter that writes to a file.\nfunc newFileExporter(w io.Writer) (sdktrace.SpanExporter, error) {\n\treturn stdouttrace.New(\n\t\tstdouttrace.WithWriter(w),\n\t\tstdouttrace.WithPrettyPrint(),\n\t)\n}\n\nfunc otelGRPC(ctx context.Context, e OTELGRPC) (sdktrace.SpanExporter, error) { //(*otlptrace.Exporter, error) {\n\texp, err := otlptrace.New(\n\t\tctx,\n\t\totlptracegrpc.NewClient(\n\t\t\totlptracegrpc.WithInsecure(),\n\t\t\totlptracegrpc.WithEndpoint(e.Addr),\n\t\t\totlptracegrpc.WithDialOption(grpc.WithBlock()),\n\t\t),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn exp, nil\n}\n"
  },
  {
    "path": "chapter/14/petstore-operator/client/proto/buf.gen.yaml",
    "content": "version: v1\nplugins:\n  - name: go\n    out: ./\n    opt:\n      - paths=source_relative\n  - name: go-grpc\n    out: ./\n    opt:\n      - paths=source_relative\n"
  },
  {
    "path": "chapter/14/petstore-operator/client/proto/buf.yaml",
    "content": "version: v1\ndeps:\n  - buf.build/googleapis/googleapis\nlint:\n  use:\n    - DEFAULT\nbreaking:\n  use:\n    - FILE\n"
  },
  {
    "path": "chapter/14/petstore-operator/client/proto/petstore.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.25.0-devel\n// \tprotoc        v3.18.0\n// source: petstore.proto\n\npackage proto\n\nimport (\n\tdate \"google.golang.org/genproto/googleapis/type/date\"\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\n// Desribes the type of pets.\ntype PetType int32\n\nconst (\n\t// The type was not set.\n\tPetType_PTUnknown PetType = 0\n\t// The pet is a canine.\n\tPetType_PTCanine PetType = 1\n\t// The pet is a feline.\n\tPetType_PTFeline PetType = 2\n\t// The pet is a bird.\n\tPetType_PTBird PetType = 3\n\t// The pet is a reptile.\n\tPetType_PTReptile PetType = 4\n)\n\n// Enum value maps for PetType.\nvar (\n\tPetType_name = map[int32]string{\n\t\t0: \"PTUnknown\",\n\t\t1: \"PTCanine\",\n\t\t2: \"PTFeline\",\n\t\t3: \"PTBird\",\n\t\t4: \"PTReptile\",\n\t}\n\tPetType_value = map[string]int32{\n\t\t\"PTUnknown\": 0,\n\t\t\"PTCanine\":  1,\n\t\t\"PTFeline\":  2,\n\t\t\"PTBird\":    3,\n\t\t\"PTReptile\": 4,\n\t}\n)\n\nfunc (x PetType) Enum() *PetType {\n\tp := new(PetType)\n\t*p = x\n\treturn p\n}\n\nfunc (x PetType) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (PetType) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_petstore_proto_enumTypes[0].Descriptor()\n}\n\nfunc (PetType) Type() protoreflect.EnumType {\n\treturn &file_petstore_proto_enumTypes[0]\n}\n\nfunc (x PetType) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use PetType.Descriptor instead.\nfunc (PetType) EnumDescriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{0}\n}\n\n// Types of OTEL sampling we support.\ntype SamplerType int32\n\nconst (\n\tSamplerType_STUnknown SamplerType = 0\n\tSamplerType_STNever   SamplerType = 1\n\tSamplerType_STAlways  SamplerType = 2\n\tSamplerType_STFloat   SamplerType = 3\n)\n\n// Enum value maps for SamplerType.\nvar (\n\tSamplerType_name = map[int32]string{\n\t\t0: \"STUnknown\",\n\t\t1: \"STNever\",\n\t\t2: \"STAlways\",\n\t\t3: \"STFloat\",\n\t}\n\tSamplerType_value = map[string]int32{\n\t\t\"STUnknown\": 0,\n\t\t\"STNever\":   1,\n\t\t\"STAlways\":  2,\n\t\t\"STFloat\":   3,\n\t}\n)\n\nfunc (x SamplerType) Enum() *SamplerType {\n\tp := new(SamplerType)\n\t*p = x\n\treturn p\n}\n\nfunc (x SamplerType) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (SamplerType) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_petstore_proto_enumTypes[1].Descriptor()\n}\n\nfunc (SamplerType) Type() protoreflect.EnumType {\n\treturn &file_petstore_proto_enumTypes[1]\n}\n\nfunc (x SamplerType) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use SamplerType.Descriptor instead.\nfunc (SamplerType) EnumDescriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{1}\n}\n\n// Represents a range of dates.\ntype DateRange struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// When to start the range, this is inclusive.\n\tStart *date.Date `protobuf:\"bytes,1,opt,name=start,proto3\" json:\"start,omitempty\"`\n\t// When to end the range, this is exclusive.\n\tEnd *date.Date `protobuf:\"bytes,2,opt,name=end,proto3\" json:\"end,omitempty\"`\n}\n\nfunc (x *DateRange) Reset() {\n\t*x = DateRange{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DateRange) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DateRange) ProtoMessage() {}\n\nfunc (x *DateRange) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_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 DateRange.ProtoReflect.Descriptor instead.\nfunc (*DateRange) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *DateRange) GetStart() *date.Date {\n\tif x != nil {\n\t\treturn x.Start\n\t}\n\treturn nil\n}\n\nfunc (x *DateRange) GetEnd() *date.Date {\n\tif x != nil {\n\t\treturn x.End\n\t}\n\treturn nil\n}\n\n// Represents a unique pet.\ntype Pet struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// A UUIDv4 for this pet. This can never be set on an AddPet().\n\tId string `protobuf:\"bytes,1,opt,name=id,proto3\" json:\"id,omitempty\"`\n\t// The name of the pet.\n\tName string `protobuf:\"bytes,2,opt,name=name,proto3\" json:\"name,omitempty\"`\n\t// The type of pet.\n\tType PetType `protobuf:\"varint,3,opt,name=type,proto3,enum=petstore.PetType\" json:\"type,omitempty\"`\n\t// The pet's birthday.\n\tBirthday *date.Date `protobuf:\"bytes,4,opt,name=birthday,proto3\" json:\"birthday,omitempty\"`\n}\n\nfunc (x *Pet) Reset() {\n\t*x = Pet{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Pet) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Pet) ProtoMessage() {}\n\nfunc (x *Pet) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_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 Pet.ProtoReflect.Descriptor instead.\nfunc (*Pet) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *Pet) GetId() string {\n\tif x != nil {\n\t\treturn x.Id\n\t}\n\treturn \"\"\n}\n\nfunc (x *Pet) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\nfunc (x *Pet) GetType() PetType {\n\tif x != nil {\n\t\treturn x.Type\n\t}\n\treturn PetType_PTUnknown\n}\n\nfunc (x *Pet) GetBirthday() *date.Date {\n\tif x != nil {\n\t\treturn x.Birthday\n\t}\n\treturn nil\n}\n\n// The request used to add a pets to the system.\ntype AddPetsReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The pet information to add. Pet.id must not be set.\n\tPets []*Pet `protobuf:\"bytes,1,rep,name=pets,proto3\" json:\"pets,omitempty\"`\n}\n\nfunc (x *AddPetsReq) Reset() {\n\t*x = AddPetsReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AddPetsReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AddPetsReq) ProtoMessage() {}\n\nfunc (x *AddPetsReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[2]\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 AddPetsReq.ProtoReflect.Descriptor instead.\nfunc (*AddPetsReq) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *AddPetsReq) GetPets() []*Pet {\n\tif x != nil {\n\t\treturn x.Pets\n\t}\n\treturn nil\n}\n\n// The response do AddPets().\ntype AddPetsResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The IDs of the pets that were added.\n\tIds []string `protobuf:\"bytes,1,rep,name=ids,proto3\" json:\"ids,omitempty\"`\n}\n\nfunc (x *AddPetsResp) Reset() {\n\t*x = AddPetsResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AddPetsResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AddPetsResp) ProtoMessage() {}\n\nfunc (x *AddPetsResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[3]\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 AddPetsResp.ProtoReflect.Descriptor instead.\nfunc (*AddPetsResp) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{3}\n}\n\nfunc (x *AddPetsResp) GetIds() []string {\n\tif x != nil {\n\t\treturn x.Ids\n\t}\n\treturn nil\n}\n\n// The request used to update pets in the system.\ntype UpdatePetsReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The pet information to update. Pet.id must be set.\n\tPets []*Pet `protobuf:\"bytes,1,rep,name=pets,proto3\" json:\"pets,omitempty\"`\n}\n\nfunc (x *UpdatePetsReq) Reset() {\n\t*x = UpdatePetsReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[4]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *UpdatePetsReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*UpdatePetsReq) ProtoMessage() {}\n\nfunc (x *UpdatePetsReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[4]\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 UpdatePetsReq.ProtoReflect.Descriptor instead.\nfunc (*UpdatePetsReq) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (x *UpdatePetsReq) GetPets() []*Pet {\n\tif x != nil {\n\t\treturn x.Pets\n\t}\n\treturn nil\n}\n\n// The response do UpdatePets().\ntype UpdatePetsResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *UpdatePetsResp) Reset() {\n\t*x = UpdatePetsResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[5]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *UpdatePetsResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*UpdatePetsResp) ProtoMessage() {}\n\nfunc (x *UpdatePetsResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[5]\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 UpdatePetsResp.ProtoReflect.Descriptor instead.\nfunc (*UpdatePetsResp) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{5}\n}\n\n// Used to indicate which pets to delete. This is an all or nothing request.\ntype DeletePetsReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The IDs of the pets to delete.\n\tIds []string `protobuf:\"bytes,1,rep,name=ids,proto3\" json:\"ids,omitempty\"`\n}\n\nfunc (x *DeletePetsReq) Reset() {\n\t*x = DeletePetsReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[6]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DeletePetsReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DeletePetsReq) ProtoMessage() {}\n\nfunc (x *DeletePetsReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[6]\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 DeletePetsReq.ProtoReflect.Descriptor instead.\nfunc (*DeletePetsReq) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{6}\n}\n\nfunc (x *DeletePetsReq) GetIds() []string {\n\tif x != nil {\n\t\treturn x.Ids\n\t}\n\treturn nil\n}\n\n// The response to a DeletePet().\ntype DeletePetsResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *DeletePetsResp) Reset() {\n\t*x = DeletePetsResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[7]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DeletePetsResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DeletePetsResp) ProtoMessage() {}\n\nfunc (x *DeletePetsResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[7]\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 DeletePetsResp.ProtoReflect.Descriptor instead.\nfunc (*DeletePetsResp) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{7}\n}\n\n// The request to search for pets.\ntype SearchPetsReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Pet names to filter by.\n\tNames []string `protobuf:\"bytes,1,rep,name=names,proto3\" json:\"names,omitempty\"`\n\t// Pet types to filter by.\n\tTypes []PetType `protobuf:\"varint,2,rep,packed,name=types,proto3,enum=petstore.PetType\" json:\"types,omitempty\"`\n\t// Birthdays to filter by.\n\tBirthdateRange *DateRange `protobuf:\"bytes,3,opt,name=birthdate_range,json=birthdateRange,proto3\" json:\"birthdate_range,omitempty\"`\n}\n\nfunc (x *SearchPetsReq) Reset() {\n\t*x = SearchPetsReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[8]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *SearchPetsReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*SearchPetsReq) ProtoMessage() {}\n\nfunc (x *SearchPetsReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[8]\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 SearchPetsReq.ProtoReflect.Descriptor instead.\nfunc (*SearchPetsReq) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{8}\n}\n\nfunc (x *SearchPetsReq) GetNames() []string {\n\tif x != nil {\n\t\treturn x.Names\n\t}\n\treturn nil\n}\n\nfunc (x *SearchPetsReq) GetTypes() []PetType {\n\tif x != nil {\n\t\treturn x.Types\n\t}\n\treturn nil\n}\n\nfunc (x *SearchPetsReq) GetBirthdateRange() *DateRange {\n\tif x != nil {\n\t\treturn x.BirthdateRange\n\t}\n\treturn nil\n}\n\ntype Sampler struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The type of sampling to change to.\n\tType SamplerType `protobuf:\"varint,1,opt,name=type,proto3,enum=petstore.SamplerType\" json:\"type,omitempty\"`\n\t// This is the sampling rate if type == STFloat. Values must be\n\t// > 0 and <= 1.0 .\n\tFloatValue float64 `protobuf:\"fixed64,2,opt,name=float_value,json=floatValue,proto3\" json:\"float_value,omitempty\"`\n}\n\nfunc (x *Sampler) Reset() {\n\t*x = Sampler{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[9]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Sampler) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Sampler) ProtoMessage() {}\n\nfunc (x *Sampler) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[9]\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 Sampler.ProtoReflect.Descriptor instead.\nfunc (*Sampler) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{9}\n}\n\nfunc (x *Sampler) GetType() SamplerType {\n\tif x != nil {\n\t\treturn x.Type\n\t}\n\treturn SamplerType_STUnknown\n}\n\nfunc (x *Sampler) GetFloatValue() float64 {\n\tif x != nil {\n\t\treturn x.FloatValue\n\t}\n\treturn 0\n}\n\n// Used to request we change the OTEL sampling.\ntype ChangeSamplerReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tSampler *Sampler `protobuf:\"bytes,1,opt,name=sampler,proto3\" json:\"sampler,omitempty\"`\n}\n\nfunc (x *ChangeSamplerReq) Reset() {\n\t*x = ChangeSamplerReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[10]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ChangeSamplerReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ChangeSamplerReq) ProtoMessage() {}\n\nfunc (x *ChangeSamplerReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[10]\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 ChangeSamplerReq.ProtoReflect.Descriptor instead.\nfunc (*ChangeSamplerReq) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{10}\n}\n\nfunc (x *ChangeSamplerReq) GetSampler() *Sampler {\n\tif x != nil {\n\t\treturn x.Sampler\n\t}\n\treturn nil\n}\n\n// The response to a sampling change.\ntype ChangeSamplerResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ChangeSamplerResp) Reset() {\n\t*x = ChangeSamplerResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_petstore_proto_msgTypes[11]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ChangeSamplerResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ChangeSamplerResp) ProtoMessage() {}\n\nfunc (x *ChangeSamplerResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_petstore_proto_msgTypes[11]\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 ChangeSamplerResp.ProtoReflect.Descriptor instead.\nfunc (*ChangeSamplerResp) Descriptor() ([]byte, []int) {\n\treturn file_petstore_proto_rawDescGZIP(), []int{11}\n}\n\nvar File_petstore_proto protoreflect.FileDescriptor\n\nvar file_petstore_proto_rawDesc = []byte{\n\t0x0a, 0x0e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x12, 0x08, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x1a, 0x16, 0x67, 0x6f, 0x6f, 0x67,\n\t0x6c, 0x65, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x22, 0x59, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12,\n\t0x27, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11,\n\t0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x44, 0x61, 0x74,\n\t0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x23, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74,\n\t0x79, 0x70, 0x65, 0x2e, 0x44, 0x61, 0x74, 0x65, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x7f, 0x0a,\n\t0x03, 0x50, 0x65, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,\n\t0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,\n\t0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,\n\t0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72,\n\t0x65, 0x2e, 0x50, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12,\n\t0x2d, 0x0a, 0x08, 0x62, 0x69, 0x72, 0x74, 0x68, 0x64, 0x61, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28,\n\t0x0b, 0x32, 0x11, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e,\n\t0x44, 0x61, 0x74, 0x65, 0x52, 0x08, 0x62, 0x69, 0x72, 0x74, 0x68, 0x64, 0x61, 0x79, 0x22, 0x2f,\n\t0x0a, 0x0a, 0x41, 0x64, 0x64, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x12, 0x21, 0x0a, 0x04,\n\t0x70, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x65, 0x74,\n\t0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x65, 0x74, 0x52, 0x04, 0x70, 0x65, 0x74, 0x73, 0x22,\n\t0x1f, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10,\n\t0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x73,\n\t0x22, 0x32, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65,\n\t0x71, 0x12, 0x21, 0x0a, 0x04, 0x70, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,\n\t0x0d, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x65, 0x74, 0x52, 0x04,\n\t0x70, 0x65, 0x74, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65,\n\t0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x21, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,\n\t0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01,\n\t0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x44, 0x65, 0x6c,\n\t0x65, 0x74, 0x65, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x8c, 0x01, 0x0a, 0x0d,\n\t0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a,\n\t0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61,\n\t0x6d, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03,\n\t0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x65,\n\t0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0f,\n\t0x62, 0x69, 0x72, 0x74, 0x68, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18,\n\t0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65,\n\t0x2e, 0x44, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0e, 0x62, 0x69, 0x72, 0x74,\n\t0x68, 0x64, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x55, 0x0a, 0x07, 0x53, 0x61,\n\t0x6d, 0x70, 0x6c, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x53,\n\t0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,\n\t0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x56, 0x61, 0x6c, 0x75,\n\t0x65, 0x22, 0x3f, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x61, 0x6d, 0x70, 0x6c,\n\t0x65, 0x72, 0x52, 0x65, 0x71, 0x12, 0x2b, 0x0a, 0x07, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72,\n\t0x65, 0x2e, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x52, 0x07, 0x73, 0x61, 0x6d, 0x70, 0x6c,\n\t0x65, 0x72, 0x22, 0x13, 0x0a, 0x11, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x61, 0x6d, 0x70,\n\t0x6c, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x2a, 0x4f, 0x0a, 0x07, 0x50, 0x65, 0x74, 0x54, 0x79,\n\t0x70, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x54, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10,\n\t0x00, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x54, 0x43, 0x61, 0x6e, 0x69, 0x6e, 0x65, 0x10, 0x01, 0x12,\n\t0x0c, 0x0a, 0x08, 0x50, 0x54, 0x46, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x10, 0x02, 0x12, 0x0a, 0x0a,\n\t0x06, 0x50, 0x54, 0x42, 0x69, 0x72, 0x64, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x54, 0x52,\n\t0x65, 0x70, 0x74, 0x69, 0x6c, 0x65, 0x10, 0x04, 0x2a, 0x44, 0x0a, 0x0b, 0x53, 0x61, 0x6d, 0x70,\n\t0x6c, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x54, 0x55, 0x6e, 0x6b,\n\t0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x4e, 0x65, 0x76, 0x65,\n\t0x72, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x54, 0x41, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x10,\n\t0x02, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x10, 0x03, 0x32, 0xd0,\n\t0x02, 0x0a, 0x08, 0x50, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x41,\n\t0x64, 0x64, 0x50, 0x65, 0x74, 0x73, 0x12, 0x14, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72,\n\t0x65, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x70,\n\t0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x65, 0x74, 0x73, 0x52,\n\t0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50,\n\t0x65, 0x74, 0x73, 0x12, 0x17, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x55,\n\t0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x70,\n\t0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65,\n\t0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65,\n\t0x74, 0x65, 0x50, 0x65, 0x74, 0x73, 0x12, 0x17, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72,\n\t0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x1a,\n\t0x18, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74,\n\t0x65, 0x50, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x38, 0x0a, 0x0a, 0x53,\n\t0x65, 0x61, 0x72, 0x63, 0x68, 0x50, 0x65, 0x74, 0x73, 0x12, 0x17, 0x2e, 0x70, 0x65, 0x74, 0x73,\n\t0x74, 0x6f, 0x72, 0x65, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x50, 0x65, 0x74, 0x73, 0x52,\n\t0x65, 0x71, 0x1a, 0x0d, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x65,\n\t0x74, 0x22, 0x00, 0x30, 0x01, 0x12, 0x4a, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53,\n\t0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72,\n\t0x65, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x52,\n\t0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x70, 0x65, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x68,\n\t0x61, 0x6e, 0x67, 0x65, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x22,\n\t0x00, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,\n\t0x50, 0x61, 0x63, 0x6b, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x2f,\n\t0x47, 0x6f, 0x2d, 0x66, 0x6f, 0x72, 0x2d, 0x44, 0x65, 0x76, 0x4f, 0x70, 0x73, 0x2f, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_petstore_proto_rawDescOnce sync.Once\n\tfile_petstore_proto_rawDescData = file_petstore_proto_rawDesc\n)\n\nfunc file_petstore_proto_rawDescGZIP() []byte {\n\tfile_petstore_proto_rawDescOnce.Do(func() {\n\t\tfile_petstore_proto_rawDescData = protoimpl.X.CompressGZIP(file_petstore_proto_rawDescData)\n\t})\n\treturn file_petstore_proto_rawDescData\n}\n\nvar file_petstore_proto_enumTypes = make([]protoimpl.EnumInfo, 2)\nvar file_petstore_proto_msgTypes = make([]protoimpl.MessageInfo, 12)\nvar file_petstore_proto_goTypes = []interface{}{\n\t(PetType)(0),              // 0: petstore.PetType\n\t(SamplerType)(0),          // 1: petstore.SamplerType\n\t(*DateRange)(nil),         // 2: petstore.DateRange\n\t(*Pet)(nil),               // 3: petstore.Pet\n\t(*AddPetsReq)(nil),        // 4: petstore.AddPetsReq\n\t(*AddPetsResp)(nil),       // 5: petstore.AddPetsResp\n\t(*UpdatePetsReq)(nil),     // 6: petstore.UpdatePetsReq\n\t(*UpdatePetsResp)(nil),    // 7: petstore.UpdatePetsResp\n\t(*DeletePetsReq)(nil),     // 8: petstore.DeletePetsReq\n\t(*DeletePetsResp)(nil),    // 9: petstore.DeletePetsResp\n\t(*SearchPetsReq)(nil),     // 10: petstore.SearchPetsReq\n\t(*Sampler)(nil),           // 11: petstore.Sampler\n\t(*ChangeSamplerReq)(nil),  // 12: petstore.ChangeSamplerReq\n\t(*ChangeSamplerResp)(nil), // 13: petstore.ChangeSamplerResp\n\t(*date.Date)(nil),         // 14: google.type.Date\n}\nvar file_petstore_proto_depIdxs = []int32{\n\t14, // 0: petstore.DateRange.start:type_name -> google.type.Date\n\t14, // 1: petstore.DateRange.end:type_name -> google.type.Date\n\t0,  // 2: petstore.Pet.type:type_name -> petstore.PetType\n\t14, // 3: petstore.Pet.birthday:type_name -> google.type.Date\n\t3,  // 4: petstore.AddPetsReq.pets:type_name -> petstore.Pet\n\t3,  // 5: petstore.UpdatePetsReq.pets:type_name -> petstore.Pet\n\t0,  // 6: petstore.SearchPetsReq.types:type_name -> petstore.PetType\n\t2,  // 7: petstore.SearchPetsReq.birthdate_range:type_name -> petstore.DateRange\n\t1,  // 8: petstore.Sampler.type:type_name -> petstore.SamplerType\n\t11, // 9: petstore.ChangeSamplerReq.sampler:type_name -> petstore.Sampler\n\t4,  // 10: petstore.PetStore.AddPets:input_type -> petstore.AddPetsReq\n\t6,  // 11: petstore.PetStore.UpdatePets:input_type -> petstore.UpdatePetsReq\n\t8,  // 12: petstore.PetStore.DeletePets:input_type -> petstore.DeletePetsReq\n\t10, // 13: petstore.PetStore.SearchPets:input_type -> petstore.SearchPetsReq\n\t12, // 14: petstore.PetStore.ChangeSampler:input_type -> petstore.ChangeSamplerReq\n\t5,  // 15: petstore.PetStore.AddPets:output_type -> petstore.AddPetsResp\n\t7,  // 16: petstore.PetStore.UpdatePets:output_type -> petstore.UpdatePetsResp\n\t9,  // 17: petstore.PetStore.DeletePets:output_type -> petstore.DeletePetsResp\n\t3,  // 18: petstore.PetStore.SearchPets:output_type -> petstore.Pet\n\t13, // 19: petstore.PetStore.ChangeSampler:output_type -> petstore.ChangeSamplerResp\n\t15, // [15:20] is the sub-list for method output_type\n\t10, // [10:15] is the sub-list for method input_type\n\t10, // [10:10] is the sub-list for extension type_name\n\t10, // [10:10] is the sub-list for extension extendee\n\t0,  // [0:10] is the sub-list for field type_name\n}\n\nfunc init() { file_petstore_proto_init() }\nfunc file_petstore_proto_init() {\n\tif File_petstore_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_petstore_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DateRange); 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_petstore_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Pet); 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_petstore_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*AddPetsReq); 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_petstore_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*AddPetsResp); 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_petstore_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*UpdatePetsReq); 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_petstore_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*UpdatePetsResp); 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_petstore_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DeletePetsReq); 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_petstore_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DeletePetsResp); 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_petstore_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*SearchPetsReq); 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_petstore_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Sampler); 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_petstore_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ChangeSamplerReq); 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_petstore_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ChangeSamplerResp); 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_petstore_proto_rawDesc,\n\t\t\tNumEnums:      2,\n\t\t\tNumMessages:   12,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_petstore_proto_goTypes,\n\t\tDependencyIndexes: file_petstore_proto_depIdxs,\n\t\tEnumInfos:         file_petstore_proto_enumTypes,\n\t\tMessageInfos:      file_petstore_proto_msgTypes,\n\t}.Build()\n\tFile_petstore_proto = out.File\n\tfile_petstore_proto_rawDesc = nil\n\tfile_petstore_proto_goTypes = nil\n\tfile_petstore_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "chapter/14/petstore-operator/client/proto/petstore.proto",
    "content": "syntax = \"proto3\";\n\npackage petstore;\n\noption go_package = \"github.com/PacktPublishing/Go-for-DevOps/proto\";\n\nimport \"google/type/date.proto\";\n\n// Desribes the type of pets.\nenum PetType {\n\t// The type was not set.\n\tPTUnknown = 0;\n\t// The pet is a canine.\n\tPTCanine = 1;\n\t// The pet is a feline.\n\tPTFeline = 2;\n\t// The pet is a bird.\n\tPTBird = 3;\n\t// The pet is a reptile.\n\tPTReptile = 4;\n}\n\n// Represents a range of dates.\nmessage DateRange {\n\t// When to start the range, this is inclusive.\n\tgoogle.type.Date start = 1;\n\t// When to end the range, this is exclusive.\n\tgoogle.type.Date end = 2;\n}\n\n// Represents a unique pet.\nmessage Pet {\n\t// A UUIDv4 for this pet. This can never be set on an AddPet().\n\tstring id = 1;\n\t// The name of the pet.\n\tstring name = 2;\n\t// The type of pet.\n\tPetType type = 3;\n\t// The pet's birthday.\n\tgoogle.type.Date birthday = 4;\n}\n\n// The request used to add a pets to the system.\nmessage AddPetsReq {\n\t// The pet information to add. Pet.id must not be set.\n\trepeated Pet pets = 1;\n}\n\n// The response do AddPets().\nmessage AddPetsResp {\n\t// The IDs of the pets that were added.\n\trepeated string ids = 1;\n}\n\n// The request used to update pets in the system.\nmessage UpdatePetsReq {\n\t// The pet information to update. Pet.id must be set.\n\trepeated Pet pets = 1;\n}\n\n// The response do UpdatePets().\nmessage UpdatePetsResp {}\n\n// Used to indicate which pets to delete. This is an all or nothing request.\nmessage DeletePetsReq {\n\t// The IDs of the pets to delete.\n\trepeated string ids = 1;\n}\n\n// The response to a DeletePet().\nmessage DeletePetsResp{}\n\n// The request to search for pets.\nmessage SearchPetsReq {\n\t// Pet names to filter by.\n\trepeated string names = 1;\n\t// Pet types to filter by.\n\trepeated PetType types = 2;\n\t// Birthdays to filter by.\n\tDateRange birthdate_range = 3;\n}\n\n// Types of OTEL sampling we support.\nenum SamplerType {\n\tSTUnknown = 0;\n\tSTNever = 1;\n\tSTAlways = 2;\n\tSTFloat = 3;\n}\n\nmessage Sampler {\n\t// The type of sampling to change to.\n\tSamplerType type = 1;\n\n\t// This is the sampling rate if type == STFloat. Values must be\n\t// > 0 and <= 1.0 .\t\n\tdouble float_value = 2;\n}\n\n// Used to request we change the OTEL sampling.\nmessage ChangeSamplerReq {\n\tSampler sampler = 1;\n}\n\n// The response to a sampling change.\nmessage ChangeSamplerResp{}\n\nservice PetStore {\n\t// Adds pets to the pet store.\n\trpc AddPets(AddPetsReq) returns (AddPetsResp) {};\n\t// Updates pets entries in the store.\n\trpc UpdatePets(UpdatePetsReq) returns (UpdatePetsResp) {};\n\t// Deletes pets from the pet store.\n\trpc DeletePets(DeletePetsReq) returns (DeletePetsResp) {};\n\t// Finds pets in the pet store.\n\trpc SearchPets(SearchPetsReq) returns (stream Pet) {};\n\n\n\t// These are for management. In real life I might break this into a new server that is\n\t// serving on SSH so that it has to be auth'd and reachable only from in my network\n\t// or only if auth'd by something.\n\n\t// Changes the OTEL sampling type.\n\trpc ChangeSampler(ChangeSamplerReq) returns (ChangeSamplerResp) {};\n}\n"
  },
  {
    "path": "chapter/14/petstore-operator/client/proto/petstore_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n\npackage proto\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// PetStoreClient is the client API for PetStore 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 PetStoreClient interface {\n\t// Adds pets to the pet store.\n\tAddPets(ctx context.Context, in *AddPetsReq, opts ...grpc.CallOption) (*AddPetsResp, error)\n\t// Updates pets entries in the store.\n\tUpdatePets(ctx context.Context, in *UpdatePetsReq, opts ...grpc.CallOption) (*UpdatePetsResp, error)\n\t// Deletes pets from the pet store.\n\tDeletePets(ctx context.Context, in *DeletePetsReq, opts ...grpc.CallOption) (*DeletePetsResp, error)\n\t// Finds pets in the pet store.\n\tSearchPets(ctx context.Context, in *SearchPetsReq, opts ...grpc.CallOption) (PetStore_SearchPetsClient, error)\n\t// Changes the OTEL sampling type.\n\tChangeSampler(ctx context.Context, in *ChangeSamplerReq, opts ...grpc.CallOption) (*ChangeSamplerResp, error)\n}\n\ntype petStoreClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewPetStoreClient(cc grpc.ClientConnInterface) PetStoreClient {\n\treturn &petStoreClient{cc}\n}\n\nfunc (c *petStoreClient) AddPets(ctx context.Context, in *AddPetsReq, opts ...grpc.CallOption) (*AddPetsResp, error) {\n\tout := new(AddPetsResp)\n\terr := c.cc.Invoke(ctx, \"/petstore.PetStore/AddPets\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *petStoreClient) UpdatePets(ctx context.Context, in *UpdatePetsReq, opts ...grpc.CallOption) (*UpdatePetsResp, error) {\n\tout := new(UpdatePetsResp)\n\terr := c.cc.Invoke(ctx, \"/petstore.PetStore/UpdatePets\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *petStoreClient) DeletePets(ctx context.Context, in *DeletePetsReq, opts ...grpc.CallOption) (*DeletePetsResp, error) {\n\tout := new(DeletePetsResp)\n\terr := c.cc.Invoke(ctx, \"/petstore.PetStore/DeletePets\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *petStoreClient) SearchPets(ctx context.Context, in *SearchPetsReq, opts ...grpc.CallOption) (PetStore_SearchPetsClient, error) {\n\tstream, err := c.cc.NewStream(ctx, &PetStore_ServiceDesc.Streams[0], \"/petstore.PetStore/SearchPets\", opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &petStoreSearchPetsClient{stream}\n\tif err := x.ClientStream.SendMsg(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := x.ClientStream.CloseSend(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn x, nil\n}\n\ntype PetStore_SearchPetsClient interface {\n\tRecv() (*Pet, error)\n\tgrpc.ClientStream\n}\n\ntype petStoreSearchPetsClient struct {\n\tgrpc.ClientStream\n}\n\nfunc (x *petStoreSearchPetsClient) Recv() (*Pet, error) {\n\tm := new(Pet)\n\tif err := x.ClientStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\nfunc (c *petStoreClient) ChangeSampler(ctx context.Context, in *ChangeSamplerReq, opts ...grpc.CallOption) (*ChangeSamplerResp, error) {\n\tout := new(ChangeSamplerResp)\n\terr := c.cc.Invoke(ctx, \"/petstore.PetStore/ChangeSampler\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// PetStoreServer is the server API for PetStore service.\n// All implementations must embed UnimplementedPetStoreServer\n// for forward compatibility\ntype PetStoreServer interface {\n\t// Adds pets to the pet store.\n\tAddPets(context.Context, *AddPetsReq) (*AddPetsResp, error)\n\t// Updates pets entries in the store.\n\tUpdatePets(context.Context, *UpdatePetsReq) (*UpdatePetsResp, error)\n\t// Deletes pets from the pet store.\n\tDeletePets(context.Context, *DeletePetsReq) (*DeletePetsResp, error)\n\t// Finds pets in the pet store.\n\tSearchPets(*SearchPetsReq, PetStore_SearchPetsServer) error\n\t// Changes the OTEL sampling type.\n\tChangeSampler(context.Context, *ChangeSamplerReq) (*ChangeSamplerResp, error)\n\tmustEmbedUnimplementedPetStoreServer()\n}\n\n// UnimplementedPetStoreServer must be embedded to have forward compatible implementations.\ntype UnimplementedPetStoreServer struct {\n}\n\nfunc (UnimplementedPetStoreServer) AddPets(context.Context, *AddPetsReq) (*AddPetsResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method AddPets not implemented\")\n}\nfunc (UnimplementedPetStoreServer) UpdatePets(context.Context, *UpdatePetsReq) (*UpdatePetsResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method UpdatePets not implemented\")\n}\nfunc (UnimplementedPetStoreServer) DeletePets(context.Context, *DeletePetsReq) (*DeletePetsResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method DeletePets not implemented\")\n}\nfunc (UnimplementedPetStoreServer) SearchPets(*SearchPetsReq, PetStore_SearchPetsServer) error {\n\treturn status.Errorf(codes.Unimplemented, \"method SearchPets not implemented\")\n}\nfunc (UnimplementedPetStoreServer) ChangeSampler(context.Context, *ChangeSamplerReq) (*ChangeSamplerResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method ChangeSampler not implemented\")\n}\nfunc (UnimplementedPetStoreServer) mustEmbedUnimplementedPetStoreServer() {}\n\n// UnsafePetStoreServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to PetStoreServer will\n// result in compilation errors.\ntype UnsafePetStoreServer interface {\n\tmustEmbedUnimplementedPetStoreServer()\n}\n\nfunc RegisterPetStoreServer(s grpc.ServiceRegistrar, srv PetStoreServer) {\n\ts.RegisterService(&PetStore_ServiceDesc, srv)\n}\n\nfunc _PetStore_AddPets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(AddPetsReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(PetStoreServer).AddPets(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/petstore.PetStore/AddPets\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(PetStoreServer).AddPets(ctx, req.(*AddPetsReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _PetStore_UpdatePets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(UpdatePetsReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(PetStoreServer).UpdatePets(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/petstore.PetStore/UpdatePets\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(PetStoreServer).UpdatePets(ctx, req.(*UpdatePetsReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _PetStore_DeletePets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(DeletePetsReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(PetStoreServer).DeletePets(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/petstore.PetStore/DeletePets\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(PetStoreServer).DeletePets(ctx, req.(*DeletePetsReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _PetStore_SearchPets_Handler(srv interface{}, stream grpc.ServerStream) error {\n\tm := new(SearchPetsReq)\n\tif err := stream.RecvMsg(m); err != nil {\n\t\treturn err\n\t}\n\treturn srv.(PetStoreServer).SearchPets(m, &petStoreSearchPetsServer{stream})\n}\n\ntype PetStore_SearchPetsServer interface {\n\tSend(*Pet) error\n\tgrpc.ServerStream\n}\n\ntype petStoreSearchPetsServer struct {\n\tgrpc.ServerStream\n}\n\nfunc (x *petStoreSearchPetsServer) Send(m *Pet) error {\n\treturn x.ServerStream.SendMsg(m)\n}\n\nfunc _PetStore_ChangeSampler_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ChangeSamplerReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(PetStoreServer).ChangeSampler(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/petstore.PetStore/ChangeSampler\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(PetStoreServer).ChangeSampler(ctx, req.(*ChangeSamplerReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// PetStore_ServiceDesc is the grpc.ServiceDesc for PetStore 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 PetStore_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"petstore.PetStore\",\n\tHandlerType: (*PetStoreServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"AddPets\",\n\t\t\tHandler:    _PetStore_AddPets_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"UpdatePets\",\n\t\t\tHandler:    _PetStore_UpdatePets_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"DeletePets\",\n\t\t\tHandler:    _PetStore_DeletePets_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"ChangeSampler\",\n\t\t\tHandler:    _PetStore_ChangeSampler_Handler,\n\t\t},\n\t},\n\tStreams: []grpc.StreamDesc{\n\t\t{\n\t\t\tStreamName:    \"SearchPets\",\n\t\t\tHandler:       _PetStore_SearchPets_Handler,\n\t\t\tServerStreams: true,\n\t\t},\n\t},\n\tMetadata: \"petstore.proto\",\n}\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/crd/bases/petstore.example.com_pets.yaml",
    "content": "---\napiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    controller-gen.kubebuilder.io/version: v0.8.0\n  creationTimestamp: null\n  name: pets.petstore.example.com\nspec:\n  group: petstore.example.com\n  names:\n    kind: Pet\n    listKind: PetList\n    plural: pets\n    singular: pet\n  scope: Namespaced\n  versions:\n  - name: v1alpha1\n    schema:\n      openAPIV3Schema:\n        description: Pet is the Schema for the pets API\n        properties:\n          apiVersion:\n            description: 'APIVersion defines the versioned schema of this representation\n              of an object. Servers should convert recognized schemas to the latest\n              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'\n            type: string\n          kind:\n            description: 'Kind is a string value representing the REST resource this\n              object represents. Servers may infer this from the endpoint the client\n              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'\n            type: string\n          metadata:\n            type: object\n          spec:\n            description: PetSpec defines the desired state of Pet\n            properties:\n              birthday:\n                description: Birthday is the date the pet was born\n                format: date-time\n                type: string\n              name:\n                description: Name is the name of the pet\n                type: string\n              type:\n                description: Type is the type of pet\n                enum:\n                - dog\n                - cat\n                - bird\n                - reptile\n                type: string\n            required:\n            - birthday\n            - name\n            - type\n            type: object\n          status:\n            description: PetStatus defines the observed state of Pet\n            properties:\n              id:\n                description: ID is the unique identifier created by the service for\n                  the pet\n                type: string\n            type: object\n        type: object\n    served: true\n    storage: true\n    subresources:\n      status: {}\nstatus:\n  acceptedNames:\n    kind: \"\"\n    plural: \"\"\n  conditions: []\n  storedVersions: []\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/crd/kustomization.yaml",
    "content": "# This kustomization.yaml is not intended to be run by itself,\n# since it depends on service name and namespace that are out of this kustomize package.\n# It should be run by config/default\nresources:\n- bases/petstore.example.com_pets.yaml\n#+kubebuilder:scaffold:crdkustomizeresource\n\npatchesStrategicMerge:\n# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.\n# patches here are for enabling the conversion webhook for each CRD\n#- patches/webhook_in_pets.yaml\n#+kubebuilder:scaffold:crdkustomizewebhookpatch\n\n# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix.\n# patches here are for enabling the CA injection for each CRD\n#- patches/cainjection_in_pets.yaml\n#+kubebuilder:scaffold:crdkustomizecainjectionpatch\n\n# the following config is for teaching kustomize how to do kustomization for CRDs.\nconfigurations:\n- kustomizeconfig.yaml\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/crd/kustomizeconfig.yaml",
    "content": "# This file is for teaching kustomize how to substitute name and namespace reference in CRD\nnameReference:\n- kind: Service\n  version: v1\n  fieldSpecs:\n  - kind: CustomResourceDefinition\n    version: v1\n    group: apiextensions.k8s.io\n    path: spec/conversion/webhook/clientConfig/service/name\n\nnamespace:\n- kind: CustomResourceDefinition\n  version: v1\n  group: apiextensions.k8s.io\n  path: spec/conversion/webhook/clientConfig/service/namespace\n  create: false\n\nvarReference:\n- path: metadata/annotations\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/crd/patches/cainjection_in_pets.yaml",
    "content": "# The following patch adds a directive for certmanager to inject CA into the CRD\napiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)\n  name: pets.petstore.example.com\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/crd/patches/webhook_in_pets.yaml",
    "content": "# The following patch enables a conversion webhook for the CRD\napiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  name: pets.petstore.example.com\nspec:\n  conversion:\n    strategy: Webhook\n    webhook:\n      clientConfig:\n        service:\n          namespace: system\n          name: webhook-service\n          path: /convert\n      conversionReviewVersions:\n      - v1\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/default/kustomization.yaml",
    "content": "# Adds namespace to all resources.\nnamespace: petstore-operator-system\n\n# Value of this field is prepended to the\n# names of all resources, e.g. a deployment named\n# \"wordpress\" becomes \"alices-wordpress\".\n# Note that it should also match with the prefix (text before '-') of the namespace\n# field above.\nnamePrefix: petstore-operator-\n\n# Labels to add to all resources and selectors.\n#commonLabels:\n#  someName: someValue\n\nbases:\n- ../crd\n- ../rbac\n- ../manager\n# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in\n# crd/kustomization.yaml\n#- ../webhook\n# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.\n#- ../certmanager\n# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.\n#- ../prometheus\n\npatchesStrategicMerge:\n# Protect the /metrics endpoint by putting it behind auth.\n# If you want your controller-manager to expose the /metrics\n# endpoint w/o any authn/z, please comment the following line.\n- manager_auth_proxy_patch.yaml\n\n# Mount the controller config file for loading manager configurations\n# through a ComponentConfig type\n#- manager_config_patch.yaml\n\n# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in\n# crd/kustomization.yaml\n#- manager_webhook_patch.yaml\n\n# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'.\n# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks.\n# 'CERTMANAGER' needs to be enabled to use ca injection\n#- webhookcainjection_patch.yaml\n\n# the following config is for teaching kustomize how to do var substitution\nvars:\n# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.\n#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR\n#  objref:\n#    kind: Certificate\n#    group: cert-manager.io\n#    version: v1\n#    name: serving-cert # this name should match the one in certificate.yaml\n#  fieldref:\n#    fieldpath: metadata.namespace\n#- name: CERTIFICATE_NAME\n#  objref:\n#    kind: Certificate\n#    group: cert-manager.io\n#    version: v1\n#    name: serving-cert # this name should match the one in certificate.yaml\n#- name: SERVICE_NAMESPACE # namespace of the service\n#  objref:\n#    kind: Service\n#    version: v1\n#    name: webhook-service\n#  fieldref:\n#    fieldpath: metadata.namespace\n#- name: SERVICE_NAME\n#  objref:\n#    kind: Service\n#    version: v1\n#    name: webhook-service\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/default/manager_auth_proxy_patch.yaml",
    "content": "# This patch inject a sidecar container which is a HTTP proxy for the\n# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: controller-manager\n  namespace: system\nspec:\n  template:\n    spec:\n      containers:\n      - name: kube-rbac-proxy\n        image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0\n        args:\n        - \"--secure-listen-address=0.0.0.0:8443\"\n        - \"--upstream=http://127.0.0.1:8080/\"\n        - \"--logtostderr=true\"\n        - \"--v=0\"\n        ports:\n        - containerPort: 8443\n          protocol: TCP\n          name: https\n        resources:\n          limits:\n            cpu: 500m\n            memory: 128Mi\n          requests:\n            cpu: 5m\n            memory: 64Mi\n      - name: manager\n        args:\n        - \"--health-probe-bind-address=:8081\"\n        - \"--metrics-bind-address=127.0.0.1:8080\"\n        - \"--leader-elect\"\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/default/manager_config_patch.yaml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: controller-manager\n  namespace: system\nspec:\n  template:\n    spec:\n      containers:\n      - name: manager\n        args:\n        - \"--config=controller_manager_config.yaml\"\n        volumeMounts:\n        - name: manager-config\n          mountPath: /controller_manager_config.yaml\n          subPath: controller_manager_config.yaml\n      volumes:\n      - name: manager-config\n        configMap:\n          name: manager-config\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/manager/controller_manager_config.yaml",
    "content": "apiVersion: controller-runtime.sigs.k8s.io/v1alpha1\nkind: ControllerManagerConfig\nhealth:\n  healthProbeBindAddress: :8081\nmetrics:\n  bindAddress: 127.0.0.1:8080\nwebhook:\n  port: 9443\nleaderElection:\n  leaderElect: true\n  resourceName: 9c3fedb1.example.com\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/manager/kustomization.yaml",
    "content": "resources:\n- manager.yaml\n\ngeneratorOptions:\n  disableNameSuffixHash: true\n\nconfigMapGenerator:\n- name: manager-config\n  files:\n  - controller_manager_config.yaml\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/manager/manager.yaml",
    "content": "apiVersion: v1\nkind: Namespace\nmetadata:\n  labels:\n    control-plane: controller-manager\n  name: system\n---\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: controller-manager\n  namespace: system\n  labels:\n    control-plane: controller-manager\nspec:\n  selector:\n    matchLabels:\n      control-plane: controller-manager\n  replicas: 1\n  template:\n    metadata:\n      annotations:\n        kubectl.kubernetes.io/default-container: manager\n      labels:\n        control-plane: controller-manager\n    spec:\n      securityContext:\n        runAsNonRoot: true\n      containers:\n      - command:\n        - /manager\n        args:\n        - --leader-elect\n        image: controller:latest\n        name: manager\n        securityContext:\n          allowPrivilegeEscalation: false\n        livenessProbe:\n          httpGet:\n            path: /healthz\n            port: 8081\n          initialDelaySeconds: 15\n          periodSeconds: 20\n        readinessProbe:\n          httpGet:\n            path: /readyz\n            port: 8081\n          initialDelaySeconds: 5\n          periodSeconds: 10\n        # TODO(user): Configure the resources accordingly based on the project requirements.\n        # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/\n        resources:\n          limits:\n            cpu: 500m\n            memory: 128Mi\n          requests:\n            cpu: 10m\n            memory: 64Mi\n      serviceAccountName: controller-manager\n      terminationGracePeriodSeconds: 10\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/manifests/kustomization.yaml",
    "content": "# These resources constitute the fully configured set of manifests\n# used to generate the 'manifests/' directory in a bundle.\nresources:\n- bases/petstore-operator.clusterserviceversion.yaml\n- ../default\n- ../samples\n- ../scorecard\n\n# [WEBHOOK] To enable webhooks, uncomment all the sections with [WEBHOOK] prefix.\n# Do NOT uncomment sections with prefix [CERTMANAGER], as OLM does not support cert-manager.\n# These patches remove the unnecessary \"cert\" volume and its manager container volumeMount.\n#patchesJson6902:\n#- target:\n#    group: apps\n#    version: v1\n#    kind: Deployment\n#    name: controller-manager\n#    namespace: system\n#  patch: |-\n#    # Remove the manager container's \"cert\" volumeMount, since OLM will create and mount a set of certs.\n#    # Update the indices in this path if adding or removing containers/volumeMounts in the manager's Deployment.\n#    - op: remove\n#      path: /spec/template/spec/containers/1/volumeMounts/0\n#    # Remove the \"cert\" volume, since OLM will create and mount a set of certs.\n#    # Update the indices in this path if adding or removing volumes in the manager's Deployment.\n#    - op: remove\n#      path: /spec/template/spec/volumes/0\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/petstore-service/service.yaml",
    "content": "---\napiVersion: v1\nkind: Namespace\nmetadata:\n  name: petstore\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: petstore-service\n  namespace: petstore\nspec:\n  selector:\n    app: petstore-service\n  ports:\n    - name: grpc\n      protocol: TCP\n      port: 6742\n      targetPort: 6742\n---\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: petstore-service\n  namespace: petstore\n  labels:\n    app: petstore-service\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: petstore-service\n  template:\n    metadata:\n      labels:\n        app: petstore-service\n    spec:\n      containers:\n        - name: service\n          image: petstore:latest\n          command: [ \"/go/bin/petstore\" ]\n          args: [ \"--localDebug\" ]\n          ports:\n            - containerPort: 6742\n              hostPort: 6742\n              name: grpc\n\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/prometheus/kustomization.yaml",
    "content": "resources:\n- monitor.yaml\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/prometheus/monitor.yaml",
    "content": "\n# Prometheus Monitor Service (Metrics)\napiVersion: monitoring.coreos.com/v1\nkind: ServiceMonitor\nmetadata:\n  labels:\n    control-plane: controller-manager\n  name: controller-manager-metrics-monitor\n  namespace: system\nspec:\n  endpoints:\n    - path: /metrics\n      port: https\n      scheme: https\n      bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token\n      tlsConfig:\n        insecureSkipVerify: true\n  selector:\n    matchLabels:\n      control-plane: controller-manager\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/rbac/auth_proxy_client_clusterrole.yaml",
    "content": "apiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n  name: metrics-reader\nrules:\n- nonResourceURLs:\n  - \"/metrics\"\n  verbs:\n  - get\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/rbac/auth_proxy_role.yaml",
    "content": "apiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n  name: proxy-role\nrules:\n- apiGroups:\n  - authentication.k8s.io\n  resources:\n  - tokenreviews\n  verbs:\n  - create\n- apiGroups:\n  - authorization.k8s.io\n  resources:\n  - subjectaccessreviews\n  verbs:\n  - create\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/rbac/auth_proxy_role_binding.yaml",
    "content": "apiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRoleBinding\nmetadata:\n  name: proxy-rolebinding\nroleRef:\n  apiGroup: rbac.authorization.k8s.io\n  kind: ClusterRole\n  name: proxy-role\nsubjects:\n- kind: ServiceAccount\n  name: controller-manager\n  namespace: system\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/rbac/auth_proxy_service.yaml",
    "content": "apiVersion: v1\nkind: Service\nmetadata:\n  labels:\n    control-plane: controller-manager\n  name: controller-manager-metrics-service\n  namespace: system\nspec:\n  ports:\n  - name: https\n    port: 8443\n    protocol: TCP\n    targetPort: https\n  selector:\n    control-plane: controller-manager\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/rbac/kustomization.yaml",
    "content": "resources:\n# All RBAC will be applied under this service account in\n# the deployment namespace. You may comment out this resource\n# if your manager will use a service account that exists at\n# runtime. Be sure to update RoleBinding and ClusterRoleBinding\n# subjects if changing service account names.\n- service_account.yaml\n- role.yaml\n- role_binding.yaml\n- leader_election_role.yaml\n- leader_election_role_binding.yaml\n# Comment the following 4 lines if you want to disable\n# the auth proxy (https://github.com/brancz/kube-rbac-proxy)\n# which protects your /metrics endpoint.\n- auth_proxy_service.yaml\n- auth_proxy_role.yaml\n- auth_proxy_role_binding.yaml\n- auth_proxy_client_clusterrole.yaml\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/rbac/leader_election_role.yaml",
    "content": "# permissions to do leader election.\napiVersion: rbac.authorization.k8s.io/v1\nkind: Role\nmetadata:\n  name: leader-election-role\nrules:\n- apiGroups:\n  - \"\"\n  resources:\n  - configmaps\n  verbs:\n  - get\n  - list\n  - watch\n  - create\n  - update\n  - patch\n  - delete\n- apiGroups:\n  - coordination.k8s.io\n  resources:\n  - leases\n  verbs:\n  - get\n  - list\n  - watch\n  - create\n  - update\n  - patch\n  - delete\n- apiGroups:\n  - \"\"\n  resources:\n  - events\n  verbs:\n  - create\n  - patch\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/rbac/leader_election_role_binding.yaml",
    "content": "apiVersion: rbac.authorization.k8s.io/v1\nkind: RoleBinding\nmetadata:\n  name: leader-election-rolebinding\nroleRef:\n  apiGroup: rbac.authorization.k8s.io\n  kind: Role\n  name: leader-election-role\nsubjects:\n- kind: ServiceAccount\n  name: controller-manager\n  namespace: system\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/rbac/pet_editor_role.yaml",
    "content": "# permissions for end users to edit pets.\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n  name: pet-editor-role\nrules:\n- apiGroups:\n  - petstore.example.com\n  resources:\n  - pets\n  verbs:\n  - create\n  - delete\n  - get\n  - list\n  - patch\n  - update\n  - watch\n- apiGroups:\n  - petstore.example.com\n  resources:\n  - pets/status\n  verbs:\n  - get\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/rbac/pet_viewer_role.yaml",
    "content": "# permissions for end users to view pets.\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n  name: pet-viewer-role\nrules:\n- apiGroups:\n  - petstore.example.com\n  resources:\n  - pets\n  verbs:\n  - get\n  - list\n  - watch\n- apiGroups:\n  - petstore.example.com\n  resources:\n  - pets/status\n  verbs:\n  - get\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/rbac/role.yaml",
    "content": "---\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n  creationTimestamp: null\n  name: manager-role\nrules:\n- apiGroups:\n  - petstore.example.com\n  resources:\n  - pets\n  verbs:\n  - create\n  - delete\n  - get\n  - list\n  - patch\n  - update\n  - watch\n- apiGroups:\n  - petstore.example.com\n  resources:\n  - pets/finalizers\n  verbs:\n  - update\n- apiGroups:\n  - petstore.example.com\n  resources:\n  - pets/status\n  verbs:\n  - get\n  - patch\n  - update\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/rbac/role_binding.yaml",
    "content": "apiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRoleBinding\nmetadata:\n  name: manager-rolebinding\nroleRef:\n  apiGroup: rbac.authorization.k8s.io\n  kind: ClusterRole\n  name: manager-role\nsubjects:\n- kind: ServiceAccount\n  name: controller-manager\n  namespace: system\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/rbac/service_account.yaml",
    "content": "apiVersion: v1\nkind: ServiceAccount\nmetadata:\n  name: controller-manager\n  namespace: system\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/samples/kustomization.yaml",
    "content": "## Append samples you want in your CSV to this file as resources ##\nresources:\n- petstore_v1alpha1_pet.yaml\n#+kubebuilder:scaffold:manifestskustomizesamples\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/samples/petstore_v1alpha1_pet.yaml",
    "content": "---\napiVersion: petstore.example.com/v1alpha1\nkind: Pet\nmetadata:\n  name: pet-sample1\nspec:\n  name: Thor\n  type: dog\n  birthday: 2021-04-01T00:00:00Z\n---\napiVersion: petstore.example.com/v1alpha1\nkind: Pet\nmetadata:\n  name: pet-sample2\nspec:\n  name: Tron\n  type: cat\n  birthday: 2020-06-25T00:00:00Z\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/scorecard/bases/config.yaml",
    "content": "apiVersion: scorecard.operatorframework.io/v1alpha3\nkind: Configuration\nmetadata:\n  name: config\nstages:\n- parallel: true\n  tests: []\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/scorecard/kustomization.yaml",
    "content": "resources:\n- bases/config.yaml\npatchesJson6902:\n- path: patches/basic.config.yaml\n  target:\n    group: scorecard.operatorframework.io\n    version: v1alpha3\n    kind: Configuration\n    name: config\n- path: patches/olm.config.yaml\n  target:\n    group: scorecard.operatorframework.io\n    version: v1alpha3\n    kind: Configuration\n    name: config\n#+kubebuilder:scaffold:patchesJson6902\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/scorecard/patches/basic.config.yaml",
    "content": "- op: add\n  path: /stages/0/tests/-\n  value:\n    entrypoint:\n    - scorecard-test\n    - basic-check-spec\n    image: quay.io/operator-framework/scorecard-test:v1.18.0\n    labels:\n      suite: basic\n      test: basic-check-spec-test\n"
  },
  {
    "path": "chapter/14/petstore-operator/config/scorecard/patches/olm.config.yaml",
    "content": "- op: add\n  path: /stages/0/tests/-\n  value:\n    entrypoint:\n    - scorecard-test\n    - olm-bundle-validation\n    image: quay.io/operator-framework/scorecard-test:v1.18.0\n    labels:\n      suite: olm\n      test: olm-bundle-validation-test\n- op: add\n  path: /stages/0/tests/-\n  value:\n    entrypoint:\n    - scorecard-test\n    - olm-crds-have-validation\n    image: quay.io/operator-framework/scorecard-test:v1.18.0\n    labels:\n      suite: olm\n      test: olm-crds-have-validation-test\n- op: add\n  path: /stages/0/tests/-\n  value:\n    entrypoint:\n    - scorecard-test\n    - olm-crds-have-resources\n    image: quay.io/operator-framework/scorecard-test:v1.18.0\n    labels:\n      suite: olm\n      test: olm-crds-have-resources-test\n- op: add\n  path: /stages/0/tests/-\n  value:\n    entrypoint:\n    - scorecard-test\n    - olm-spec-descriptors\n    image: quay.io/operator-framework/scorecard-test:v1.18.0\n    labels:\n      suite: olm\n      test: olm-spec-descriptors-test\n- op: add\n  path: /stages/0/tests/-\n  value:\n    entrypoint:\n    - scorecard-test\n    - olm-status-descriptors\n    image: quay.io/operator-framework/scorecard-test:v1.18.0\n    labels:\n      suite: olm\n      test: olm-status-descriptors-test\n"
  },
  {
    "path": "chapter/14/petstore-operator/controllers/pet_controller.go",
    "content": "/*\nCopyright 2022.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage controllers\n\nimport (\n\t\"context\"\n\n\t\"github.com/pkg/errors\"\n\t\"google.golang.org/genproto/googleapis/type/date\"\n\tapierrors \"k8s.io/apimachinery/pkg/api/errors\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"sigs.k8s.io/cluster-api/util/patch\"\n\tctrl \"sigs.k8s.io/controller-runtime\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\t\"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil\"\n\t\"sigs.k8s.io/controller-runtime/pkg/log\"\n\t\"sigs.k8s.io/controller-runtime/pkg/reconcile\"\n\n\tpetstorev1 \"github.com/PacktPublishing/Go-for-DevOps/chapter/14/petstore-operator/api/v1alpha1\"\n\tpsclient \"github.com/PacktPublishing/Go-for-DevOps/chapter/14/petstore-operator/client\"\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/14/petstore-operator/client/proto\"\n)\n\nconst (\n\tPetFinalizer = \"pet.petstore.example.com\"\n)\n\n// PetReconciler reconciles a Pet object\ntype PetReconciler struct {\n\tclient.Client\n\tScheme *runtime.Scheme\n}\n\n//+kubebuilder:rbac:groups=petstore.example.com,resources=pets,verbs=get;list;watch;create;update;patch;delete\n//+kubebuilder:rbac:groups=petstore.example.com,resources=pets/status,verbs=get;update;patch\n//+kubebuilder:rbac:groups=petstore.example.com,resources=pets/finalizers,verbs=update\n\n// Reconcile moves the current state of the pet to be the desired state described in the pet.spec.\nfunc (r *PetReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, errResult error) {\n\tlogger := log.FromContext(ctx)\n\n\tpet := &petstorev1.Pet{}\n\tif err := r.Get(ctx, req.NamespacedName, pet); err != nil {\n\t\tif apierrors.IsNotFound(err) {\n\t\t\tlogger.Info(\"object was not found\")\n\t\t\treturn reconcile.Result{}, nil\n\t\t}\n\n\t\tlogger.Error(err, \"failed to fetch pet from API server\")\n\t\t// this will cause this pet resource to be requeued\n\t\treturn ctrl.Result{}, err\n\t}\n\n\thelper, err := patch.NewHelper(pet, r.Client)\n\tif err != nil {\n\t\treturn ctrl.Result{}, errors.Wrap(err, \"failed to create patch helper\")\n\t}\n\tdefer func() {\n\t\t// patch the resource stored in the API server if something changed.\n\t\tif err := helper.Patch(ctx, pet); err != nil {\n\t\t\terrResult = err\n\t\t}\n\t}()\n\n\tif pet.DeletionTimestamp.IsZero() {\n\t\t// the pet is not marked for delete reconcile desired state\n\t\treturn r.ReconcileNormal(ctx, pet)\n\t}\n\n\t// pet has been marked for delete, so delete from the petstore\n\treturn r.ReconcileDelete(ctx, pet)\n}\n\n// ReconcileNormal will ensure the finalizer and save the desired state to the petstore.\nfunc (r *PetReconciler) ReconcileNormal(ctx context.Context, pet *petstorev1.Pet) (ctrl.Result, error) {\n\tlogger := ctrl.LoggerFrom(ctx).WithValues(\"pet\", pet.Spec.Name, \"id\", pet.Status.ID)\n\n\t// add a finalizer to ensure we clean up before K8s garbage collects\n\tlogger.Info(\"ensuring finalizer\")\n\tcontrollerutil.AddFinalizer(pet, PetFinalizer)\n\n\tpsc, err := getPetstoreClient()\n\tif err != nil {\n\t\treturn ctrl.Result{}, errors.Wrap(err, \"unable to construct petstore client\")\n\t}\n\n\tlogger.Info(\"finding pets in store\")\n\tpsPet, err := findPetInStore(ctx, psc, pet)\n\tif err != nil {\n\t\treturn ctrl.Result{}, errors.Wrap(err, \"failed trying to find pet in pet store\")\n\t}\n\n\tif psPet == nil {\n\t\tlogger.Info(\"psPet was not nil\")\n\t\t// no pet was found, so we must create a pet in the pet store\n\t\terr := createPetInStore(ctx, pet, psc)\n\t\treturn ctrl.Result{}, err\n\t}\n\n\t// pet was found, so we need to update the pet in the pet store\n\tif err := updatePetInStore(ctx, psc, pet, psPet.Pet); err != nil {\n\t\tlogger.Info(\"updating pet in store\")\n\t\treturn ctrl.Result{}, err\n\t}\n\n\treturn ctrl.Result{}, nil\n}\n\n// ReconcileDelete deletes the pet from the petstore and removes the finalizer.\nfunc (r *PetReconciler) ReconcileDelete(ctx context.Context, pet *petstorev1.Pet) (ctrl.Result, error) {\n\tpsc, err := getPetstoreClient()\n\tif err != nil {\n\t\treturn ctrl.Result{}, errors.Wrap(err, \"unable to construct petstore client\")\n\t}\n\n\tif pet.Status.ID != \"\" {\n\t\tif err := psc.DeletePets(ctx, []string{pet.Status.ID}); err != nil {\n\t\t\treturn ctrl.Result{}, errors.Wrap(err, \"failed to delete pet\")\n\t\t}\n\t}\n\n\t// remove finalizer, so K8s can garbage collect the resource.\n\tcontrollerutil.RemoveFinalizer(pet, PetFinalizer)\n\treturn ctrl.Result{}, nil\n}\n\nfunc createPetInStore(ctx context.Context, pet *petstorev1.Pet, psc *psclient.Client) error {\n\tpbPet := &pb.Pet{\n\t\tName:     pet.Spec.Name,\n\t\tType:     petTypeToProtoPetType(pet.Spec.Type),\n\t\tBirthday: timeToPbDate(pet.Spec.Birthday),\n\t}\n\tids, err := psc.AddPets(ctx, []*pb.Pet{pbPet})\n\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"failed to create new pet in store\")\n\t}\n\tpet.Status.ID = ids[0]\n\treturn nil\n}\n\nfunc updatePetInStore(ctx context.Context, psc *psclient.Client, pet *petstorev1.Pet, pbPet *pb.Pet) error {\n\tpbPet.Name = pet.Spec.Name\n\tpbPet.Type = petTypeToProtoPetType(pet.Spec.Type)\n\tpbPet.Birthday = timeToPbDate(pet.Spec.Birthday)\n\tif err := psc.UpdatePets(ctx, []*pb.Pet{pbPet}); err != nil {\n\t\treturn errors.Wrap(err, \"failed to update the pet in the store\")\n\t}\n\treturn nil\n}\n\n// SetupWithManager sets up the controller with the Manager.\nfunc (r *PetReconciler) SetupWithManager(mgr ctrl.Manager) error {\n\treturn ctrl.NewControllerManagedBy(mgr).\n\t\tFor(&petstorev1.Pet{}).\n\t\tComplete(r)\n}\n\n// findPetInStore searches the pet store for a pet that matches the custom resource pet.\nfunc findPetInStore(ctx context.Context, psc *psclient.Client, pet *petstorev1.Pet) (*psclient.Pet, error) {\n\tpetsChan, err := psc.SearchPets(ctx, &pb.SearchPetsReq{\n\t\tNames: []string{pet.Spec.Name},\n\t\tTypes: []pb.PetType{petTypeToProtoPetType(pet.Spec.Type)},\n\t})\n\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed searching for pet\")\n\t}\n\n\tfor pbPet := range petsChan {\n\t\tif pbPet.Error() != nil {\n\t\t\tlogger := ctrl.LoggerFrom(ctx)\n\t\t\tlogger.Error(err, \"search chan error\")\n\t\t\tcontinue\n\t\t}\n\n\t\tif pbPet.Id == pet.Status.ID {\n\t\t\treturn &pbPet, nil\n\t\t}\n\t}\n\n\treturn nil, nil\n}\n\nfunc petTypeToProtoPetType(petType petstorev1.PetType) pb.PetType {\n\tswitch petType {\n\tcase petstorev1.DogPetType:\n\t\treturn pb.PetType_PTCanine\n\tcase petstorev1.CatPetType:\n\t\treturn pb.PetType_PTFeline\n\tcase petstorev1.BirdPetType:\n\t\treturn pb.PetType_PTBird\n\tdefault:\n\t\treturn pb.PetType_PTReptile\n\t}\n}\n\nfunc timeToPbDate(t metav1.Time) *date.Date {\n\treturn &date.Date{\n\t\tYear:  int32(t.Year()),\n\t\tMonth: int32(t.Month()),\n\t\tDay:   int32(t.Day()),\n\t}\n}\n\nfunc getPetstoreClient() (*psclient.Client, error) {\n\treturn psclient.New(\"petstore-service.petstore.svc.cluster.local:6742\")\n}\n"
  },
  {
    "path": "chapter/14/petstore-operator/controllers/suite_test.go",
    "content": "/*\nCopyright 2022.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage controllers\n\nimport (\n\t\"path/filepath\"\n\t\"testing\"\n\n\t. \"github.com/onsi/ginkgo\"\n\t. \"github.com/onsi/gomega\"\n\t\"k8s.io/client-go/kubernetes/scheme\"\n\t\"k8s.io/client-go/rest\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\t\"sigs.k8s.io/controller-runtime/pkg/envtest\"\n\t\"sigs.k8s.io/controller-runtime/pkg/envtest/printer\"\n\tlogf \"sigs.k8s.io/controller-runtime/pkg/log\"\n\t\"sigs.k8s.io/controller-runtime/pkg/log/zap\"\n\n\tpetstorev1alpha1 \"github.com/PacktPublishing/Go-for-DevOps/chapter/14/petstore-operator/api/v1alpha1\"\n\t//+kubebuilder:scaffold:imports\n)\n\n// These tests use Ginkgo (BDD-style Go testing framework). Refer to\n// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.\n\nvar cfg *rest.Config\nvar k8sClient client.Client\nvar testEnv *envtest.Environment\n\nfunc TestAPIs(t *testing.T) {\n\tRegisterFailHandler(Fail)\n\n\tRunSpecsWithDefaultAndCustomReporters(t,\n\t\t\"Controller Suite\",\n\t\t[]Reporter{printer.NewlineReporter{}})\n}\n\nvar _ = BeforeSuite(func() {\n\tlogf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))\n\n\tBy(\"bootstrapping test environment\")\n\ttestEnv = &envtest.Environment{\n\t\tCRDDirectoryPaths:     []string{filepath.Join(\"..\", \"config\", \"crd\", \"bases\")},\n\t\tErrorIfCRDPathMissing: true,\n\t}\n\n\tcfg, err := testEnv.Start()\n\tExpect(err).NotTo(HaveOccurred())\n\tExpect(cfg).NotTo(BeNil())\n\n\terr = petstorev1alpha1.AddToScheme(scheme.Scheme)\n\tExpect(err).NotTo(HaveOccurred())\n\n\t//+kubebuilder:scaffold:scheme\n\n\tk8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})\n\tExpect(err).NotTo(HaveOccurred())\n\tExpect(k8sClient).NotTo(BeNil())\n\n}, 60)\n\nvar _ = AfterSuite(func() {\n\tBy(\"tearing down the test environment\")\n\terr := testEnv.Stop()\n\tExpect(err).NotTo(HaveOccurred())\n})\n"
  },
  {
    "path": "chapter/14/petstore-operator/go.mod",
    "content": "module github.com/PacktPublishing/Go-for-DevOps/chapter/14/petstore-operator\n\ngo 1.17\n\nrequire (\n\tgithub.com/biogo/store v0.0.0-20201120204734-aad293a2328f\n\tgithub.com/kylelemons/godebug v1.1.0\n\tgithub.com/onsi/ginkgo v1.16.5\n\tgithub.com/onsi/gomega v1.17.0\n\tgithub.com/pkg/errors v0.9.1\n\tgo.opentelemetry.io/otel v1.4.1\n\tgo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.27.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.27.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.1\n\tgo.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.4.1\n\tgo.opentelemetry.io/otel/metric v0.27.0\n\tgo.opentelemetry.io/otel/sdk v1.4.1\n\tgo.opentelemetry.io/otel/sdk/metric v0.27.0\n\tgo.opentelemetry.io/otel/trace v1.4.1\n\tgoogle.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2\n\tgoogle.golang.org/grpc v1.44.0\n\tgoogle.golang.org/protobuf v1.27.1\n\tk8s.io/apimachinery v0.23.0\n\tk8s.io/client-go v0.23.0\n\tsigs.k8s.io/cluster-api v1.1.2\n\tsigs.k8s.io/controller-runtime v0.11.1\n)\n\nrequire (\n\tcloud.google.com/go v0.93.3 // indirect\n\tgithub.com/Azure/go-autorest v14.2.0+incompatible // indirect\n\tgithub.com/Azure/go-autorest/autorest v0.11.18 // indirect\n\tgithub.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect\n\tgithub.com/Azure/go-autorest/autorest/date v0.3.0 // indirect\n\tgithub.com/Azure/go-autorest/logger v0.2.1 // indirect\n\tgithub.com/Azure/go-autorest/tracing v0.6.0 // indirect\n\tgithub.com/beorn7/perks v1.0.1 // indirect\n\tgithub.com/blang/semver v3.5.1+incompatible // indirect\n\tgithub.com/cenkalti/backoff/v4 v4.1.2 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.1.1 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/evanphx/json-patch v4.12.0+incompatible // indirect\n\tgithub.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect\n\tgithub.com/fsnotify/fsnotify v1.5.1 // indirect\n\tgithub.com/go-logr/logr v1.2.2 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/go-logr/zapr v1.2.0 // indirect\n\tgithub.com/gobuffalo/flect v0.2.4 // 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/google/go-cmp v0.5.7 // indirect\n\tgithub.com/google/gofuzz v1.2.0 // indirect\n\tgithub.com/google/uuid v1.1.2 // indirect\n\tgithub.com/googleapis/gnostic v0.5.5 // indirect\n\tgithub.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect\n\tgithub.com/imdario/mergo v0.3.12 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // 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/nxadm/tail v1.4.8 // indirect\n\tgithub.com/prometheus/client_golang v1.11.0 // indirect\n\tgithub.com/prometheus/client_model v0.2.0 // indirect\n\tgithub.com/prometheus/common v0.28.0 // indirect\n\tgithub.com/prometheus/procfs v0.6.0 // indirect\n\tgithub.com/spf13/pflag v1.0.5 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.1 // indirect\n\tgo.opentelemetry.io/otel/internal/metric v0.27.0 // indirect\n\tgo.opentelemetry.io/proto/otlp v0.12.0 // indirect\n\tgo.uber.org/atomic v1.7.0 // indirect\n\tgo.uber.org/multierr v1.6.0 // indirect\n\tgo.uber.org/zap v1.19.1 // indirect\n\tgolang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect\n\tgolang.org/x/net v0.0.0-20210825183410-e898025ed96a // indirect\n\tgolang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect\n\tgolang.org/x/sys v0.0.0-20211029165221-6e7872819dc8 // indirect\n\tgolang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect\n\tgolang.org/x/text v0.3.7 // indirect\n\tgolang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect\n\tgomodules.xyz/jsonpatch/v2 v2.2.0 // indirect\n\tgoogle.golang.org/appengine v1.6.7 // indirect\n\tgopkg.in/inf.v0 v0.9.1 // indirect\n\tgopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect\n\tk8s.io/api v0.23.0 // indirect\n\tk8s.io/apiextensions-apiserver v0.23.0 // indirect\n\tk8s.io/component-base v0.23.0 // indirect\n\tk8s.io/klog/v2 v2.30.0 // indirect\n\tk8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect\n\tk8s.io/utils v0.0.0-20210930125809-cb0fa318a74b // indirect\n\tsigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect\n\tsigs.k8s.io/structured-merge-diff/v4 v4.2.0 // indirect\n\tsigs.k8s.io/yaml v1.3.0 // indirect\n)\n"
  },
  {
    "path": "chapter/14/petstore-operator/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 v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=\ncloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=\ncloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=\ncloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=\ncloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=\ncloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=\ncloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=\ncloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=\ncloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=\ncloud.google.com/go v0.93.3 h1:wPBktZFzYBcCZVARvwVKqH1uEj+aLXofJEtrb4oOsio=\ncloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=\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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=\ncloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU=\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/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=\ngithub.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=\ngithub.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=\ngithub.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=\ngithub.com/Azure/go-autorest/autorest v0.11.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM=\ngithub.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=\ngithub.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=\ngithub.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=\ngithub.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=\ngithub.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=\ngithub.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=\ngithub.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=\ngithub.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=\ngithub.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=\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/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=\ngithub.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=\ngithub.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=\ngithub.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=\ngithub.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=\ngithub.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=\ngithub.com/ajeddeloh/go-json v0.0.0-20160803184958-73d058cf8437/go.mod h1:otnto4/Icqn88WCcM4bhIJNSgsh9VLBuspyyCfvof9c=\ngithub.com/ajeddeloh/go-json v0.0.0-20200220154158-5ae607161559/go.mod h1:otnto4/Icqn88WCcM4bhIJNSgsh9VLBuspyyCfvof9c=\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/alecthomas/units v0.0.0-20210208195552-ff826a37aa15/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e h1:GCzyKMDDjSGnlpl3clrdAK7I1AaVoaiKDOYkUzChZzg=\ngithub.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\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/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=\ngithub.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=\ngithub.com/aws/aws-sdk-go v1.8.39/go.mod h1:ZRmQr0FajVIyZ4ZzBYKG5P3ZqPz9IHG41ZoMu1ADI3k=\ngithub.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=\ngithub.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=\ngithub.com/benbjohnson/clock v1.3.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/biogo/store v0.0.0-20201120204734-aad293a2328f h1:+6okTAeUsUrdQr/qN7fIODzowrjjCrnJDg/gkYqcSXY=\ngithub.com/biogo/store v0.0.0-20201120204734-aad293a2328f/go.mod h1:z52shMwD6SGwRg2iYFjjDwX5Ene4ENTw6HfXraUy/08=\ngithub.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=\ngithub.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=\ngithub.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=\ngithub.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=\ngithub.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=\ngithub.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=\ngithub.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=\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 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=\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/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-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=\ngithub.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=\ngithub.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=\ngithub.com/coredns/caddy v1.1.0 h1:ezvsPrT/tA/7pYDBZxu0cT0VmWk75AfIaf6GSYCNMf0=\ngithub.com/coredns/caddy v1.1.0/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4=\ngithub.com/coredns/corefile-migration v1.0.14 h1:Tz3WZhoj2NdP8drrQH86NgnCng+VrPjNeg2Oe1ALKag=\ngithub.com/coredns/corefile-migration v1.0.14/go.mod h1:XnhgULOEouimnzgn0t4WPuFDN2/PJQcTxdWKC5eXNGE=\ngithub.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=\ngithub.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=\ngithub.com/coreos/go-semver v0.1.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/creack/pty v1.1.11/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/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=\ngithub.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=\ngithub.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=\ngithub.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=\ngithub.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46/go.mod h1:esf2rsHFNlZlxsqsZDojNBcnNs5REqIvRrWRHqX0vEU=\ngithub.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=\ngithub.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=\ngithub.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=\ngithub.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=\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.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=\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/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=\ngithub.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=\ngithub.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=\ngithub.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=\ngithub.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=\ngithub.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=\ngithub.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=\ngithub.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=\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/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=\ngithub.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/flatcar-linux/container-linux-config-transpiler v0.9.2/go.mod h1:AGVTulMzeIKwurV9ExYH3UiokET1Ur65g+EIeRDMwzM=\ngithub.com/flatcar-linux/ignition v0.36.1/go.mod h1:0jS5n4AopgOdwgi7QDo5MFgkMx/fQUDYjuxlGJC1Txg=\ngithub.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=\ngithub.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=\ngithub.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c=\ngithub.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=\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/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=\ngithub.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=\ngithub.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=\ngithub.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=\ngithub.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=\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-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=\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-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-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=\ngithub.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=\ngithub.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=\ngithub.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=\ngithub.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk=\ngithub.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro=\ngithub.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=\ngithub.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=\ngithub.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=\ngithub.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=\ngithub.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=\ngithub.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=\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/gobuffalo/flect v0.2.4 h1:BSYA8+T60cdyq+vynaSUjqSVI9mDEg9ZfQUXKmfjo4I=\ngithub.com/gobuffalo/flect v0.2.4/go.mod h1:1ZyCLIbg0YD7sDkzvFdPoOydPtD8y9JQnrOROolUcM8=\ngithub.com/godbus/dbus v0.0.0-20181025153459-66d97aec3384/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=\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.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=\ngithub.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\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/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\ngithub.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=\ngithub.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=\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.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=\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.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=\ngithub.com/google/cel-go v0.9.0 h1:u1hg7lcZ/XWw2d3aV1jFS30ijQQ6q0/h1C2ZBeBD1gY=\ngithub.com/google/cel-go v0.9.0/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w=\ngithub.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA=\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.2/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/go-github/v33 v33.0.0/go.mod h1:GMdDnVZY/2TsWgp/lkYnpSAh6TrzhANBBwm6k6TTEXg=\ngithub.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=\ngithub.com/google/gofuzz v1.2.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/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=\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/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=\ngithub.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=\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/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=\ngithub.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=\ngithub.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=\ngithub.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=\ngithub.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=\ngithub.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=\ngithub.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=\ngithub.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/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-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\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-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.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=\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/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=\ngithub.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=\ngithub.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=\ngithub.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=\ngithub.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=\ngithub.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=\ngithub.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=\ngithub.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=\ngithub.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=\ngithub.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=\ngithub.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\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.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/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/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=\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/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/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=\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/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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=\ngithub.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=\ngithub.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=\ngithub.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=\ngithub.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=\ngithub.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=\ngithub.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=\ngithub.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\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-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-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=\ngithub.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=\ngithub.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=\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.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=\ngithub.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo=\ngithub.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=\ngithub.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A=\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/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=\ngithub.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=\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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=\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 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=\ngithub.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=\ngithub.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=\ngithub.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=\ngithub.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=\ngithub.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=\ngithub.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=\ngithub.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=\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.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE=\ngithub.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=\ngithub.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=\ngithub.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=\ngithub.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pborman/uuid v0.0.0-20170612153648-e790cca94e6c/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=\ngithub.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=\ngithub.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=\ngithub.com/pin/tftp v2.1.0+incompatible/go.mod h1:xVpZOMCXTy+A5QMjEVN0Glwa1sUvaJhFXbr/aAxuxGY=\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/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=\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/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=\ngithub.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=\ngithub.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=\ngithub.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=\ngithub.com/prometheus/client_golang v1.11.0/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.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\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.28.0 h1:vGVfV9KrDTvWt5boZO0I19g2E3CsWfpPPKZM9dt3mEw=\ngithub.com/prometheus/common v0.28.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.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=\ngithub.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=\ngithub.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=\ngithub.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=\ngithub.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=\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/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=\ngithub.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=\ngithub.com/sigma/bdoor v0.0.0-20160202064022-babf2a4017b0/go.mod h1:WBu7REWbxC/s/J06jsk//d+9DOz9BbsmcIrimuGRFbs=\ngithub.com/sigma/vmw-guestinfo v0.0.0-20160204083807-95dd4126d6e8/go.mod h1:JrRFFC0veyh0cibh0DAhriSY7/gV3kDdNaVUOmfx01U=\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.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=\ngithub.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=\ngithub.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=\ngithub.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=\ngithub.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=\ngithub.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=\ngithub.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=\ngithub.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=\ngithub.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=\ngithub.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4=\ngithub.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=\ngithub.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=\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 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=\ngithub.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=\ngithub.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U=\ngithub.com/vmware/vmw-guestinfo v0.0.0-20170707015358-25eff159a728/go.mod h1:x9oS4Wk2s2u4tS29nEaDLdzvuHdB19CvSGJjPgkZJNk=\ngithub.com/vmware/vmw-ovflib v0.0.0-20170608004843-1f217b9dc714/go.mod h1:jiPk45kn7klhByRvUq5i2vo1RtHKBHj+iWGFpxbXuuI=\ngithub.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\ngithub.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=\ngithub.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=\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=\ngithub.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=\ngo.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=\ngo.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=\ngo.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=\ngo.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=\ngo.etcd.io/etcd/client/v3 v3.5.1/go.mod h1:OnjH4M8OnAotwaB2l9bVgZzRFKru7/ZMoS46OtKyd3Q=\ngo.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE=\ngo.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc=\ngo.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4=\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.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=\ngo.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=\ngo.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4=\ngo.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=\ngo.opentelemetry.io/otel v1.4.0/go.mod h1:jeAqMFKy2uLIxCtKxoFj0FAL5zAPKQagc3+GtBWakzk=\ngo.opentelemetry.io/otel v1.4.1 h1:QbINgGDDcoQUoMJa2mMaWno49lja9sHwp6aoa2n3a4g=\ngo.opentelemetry.io/otel v1.4.1/go.mod h1:StM6F/0fSwpd8dKWDCdRr7uRvEPYdW0hBSlbdTiUde4=\ngo.opentelemetry.io/otel/exporters/otlp v0.20.0 h1:PTNgq9MRmQqqJY0REVbZFvwkYOA85vbdQU/nVfxDyqg=\ngo.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.1 h1:imIM3vRDMyZK1ypQlQlO+brE22I9lRhJsBDXpDWjlz8=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.1/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.27.0 h1:t1aPfMj5oZzv2EaRmdC2QPQg1a7MaBjraOh4Hjwuia8=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.27.0/go.mod h1:aZnoYVx7GIuMROciGC3cjZhYxMD/lKroRJUnFY0afu0=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.27.0 h1:RJURCSrqUjJiCY3GuFCVP2EPKOQLwNXQ4FI3aH2KoHg=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.27.0/go.mod h1:LIc1eCpkU94tPnXxH40ya41Oyxm7sL+oDvxCYPFpnV8=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1 h1:WPpPsAAs8I2rA47v5u0558meKmmwm1Dj99ZbqCV8sZ8=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1/go.mod h1:o5RW5o2pKpJLD5dNTCmjF1DorYwMeFJmb/rKr5sLaa8=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.1 h1:AxqDiGk8CorEXStMDZF5Hz9vo9Z7ZZ+I5m8JRl/ko40=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.4.1/go.mod h1:c6E4V3/U+miqjs/8l950wggHGL1qzlp0Ypj9xoGrPqo=\ngo.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.4.1 h1:yaXaoJjXaJqRnsfW9HrN7pGb7bzcEn31Rk6yo2LFaWo=\ngo.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.4.1/go.mod h1:BFiGsTMZdqtxufux8ANXuMeRz9dMPVFdJZadUWDFD7o=\ngo.opentelemetry.io/otel/internal/metric v0.27.0 h1:9dAVGAfFiiEq5NVB9FUJ5et+btbDQAUIJehJ+ikyryk=\ngo.opentelemetry.io/otel/internal/metric v0.27.0/go.mod h1:n1CVxRqKqYZtqyTh9U/onvKapPGv7y/rpyOTI+LFNzw=\ngo.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=\ngo.opentelemetry.io/otel/metric v0.27.0 h1:HhJPsGhJoKRSegPQILFbODU56NS/L1UE4fS1sC5kIwQ=\ngo.opentelemetry.io/otel/metric v0.27.0/go.mod h1:raXDJ7uP2/Jc0nVZWQjJtzoyssOYWu/+pjZqRzfvZ7g=\ngo.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=\ngo.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=\ngo.opentelemetry.io/otel/sdk v1.4.0/go.mod h1:71GJPNJh4Qju6zJuYl1CrYtXbrgfau/M9UAggqiy1UE=\ngo.opentelemetry.io/otel/sdk v1.4.1 h1:J7EaW71E0v87qflB4cDolaqq3AcujGrtyIPGQoZOB0Y=\ngo.opentelemetry.io/otel/sdk v1.4.1/go.mod h1:NBwHDgDIBYjwK2WNu1OPgsIc2IJzmBXNnvIJxJc8BpE=\ngo.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE=\ngo.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE=\ngo.opentelemetry.io/otel/sdk/metric v0.27.0 h1:CDEu96Js5IP7f4bJ8eimxF09V5hKYmE7CeyKSjmAL1s=\ngo.opentelemetry.io/otel/sdk/metric v0.27.0/go.mod h1:lOgrT5C3ORdbqp2LsDrx+pBj6gbZtQ5Omk27vH3EaW0=\ngo.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=\ngo.opentelemetry.io/otel/trace v1.4.0/go.mod h1:uc3eRsqDfWs9R7b92xbQbU42/eTNz4N+gLP8qJCi4aE=\ngo.opentelemetry.io/otel/trace v1.4.1 h1:O+16qcdTrT7zxv2J6GejTPFinSwA++cYerC5iSiF8EQ=\ngo.opentelemetry.io/otel/trace v1.4.1/go.mod h1:iYEVbroFCNut9QkwEczV9vMRPHNKSSwYZjulEtsmhFc=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.opentelemetry.io/proto/otlp v0.12.0 h1:CMJ/3Wp7iOWES+CYLfnBv+DVmPbB+kmy9PJ92XvlR6c=\ngo.opentelemetry.io/proto/otlp v0.12.0/go.mod h1:TsIjwGWIx5VFYv9KGVlOpxoBl5Dy+63SUguV7GGvlSQ=\ngo.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=\ngo.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=\ngo.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=\ngo.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=\ngo.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=\ngo.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=\ngo4.org v0.0.0-20160314031811-03efcb870d84/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=\ngo4.org v0.0.0-20201209231011-d4a079459e60/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190820162420-60c769a6c586/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-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=\ngolang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/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/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\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-20201208152925-83fdc39ff7b5/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.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.1/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-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190320064053-1272bf9dcd53/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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\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-20190827160401-ba9fcec4b297/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-20201031054903-ff519b6c9102/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-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=\ngolang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw=\ngolang.org/x/net v0.0.0-20210825183410-e898025ed96a/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-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=\ngolang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/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/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-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/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-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-20191002063906-3421d5a6bb1c/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-20200124204421-9fbb57f87de9/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-20200519105757-fe76b779f299/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-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/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-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/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-20210423185535-09eb48e85fd7/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-20210514084401-e8d321eab015/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-20210603125802-9665404d3644/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-20210616094352-59db8d763f22/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-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211029165221-6e7872819dc8 h1:M69LAlWZCshgp0QSzyDcSsSIejIEeuaCVpmwcKwyLMk=\ngolang.org/x/sys v0.0.0-20211029165221-6e7872819dc8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=\ngolang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\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.1-0.20190321115727-fe223c5a2583/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\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.4/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-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=\ngolang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/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-20181030221726-6c7e314b6563/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-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-20190624222133-a101b041ded4/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-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/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-20200505023115-26f46d2f7ef8/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-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=\ngolang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=\ngolang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM=\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=\ngomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY=\ngomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY=\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/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=\ngoogle.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=\ngoogle.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=\ngoogle.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=\ngoogle.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=\ngoogle.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=\ngoogle.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=\ngoogle.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=\ngoogle.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=\ngoogle.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=\ngoogle.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=\ngoogle.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=\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/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=\ngoogle.golang.org/appengine v1.6.7/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-20200423170343-7949de9c1215/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-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201102152239-715cce707fb0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=\ngoogle.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=\ngoogle.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=\ngoogle.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=\ngoogle.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=\ngoogle.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=\ngoogle.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=\ngoogle.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=\ngoogle.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=\ngoogle.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=\ngoogle.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=\ngoogle.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=\ngoogle.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=\ngoogle.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2 h1:NHN4wOCScVzKhPenJ2dt+BTs3X/XkBVI/Rh4iDt55T8=\ngoogle.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/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.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\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.31.1/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.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=\ngoogle.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=\ngoogle.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=\ngoogle.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=\ngoogle.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg=\ngoogle.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=\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 h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=\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/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=\ngopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=\ngopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\ngopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=\ngopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=\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-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=\ngotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=\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=\nk8s.io/api v0.23.0 h1:WrL1gb73VSC8obi8cuYETJGXEoFNEh3LU0Pt+Sokgro=\nk8s.io/api v0.23.0/go.mod h1:8wmDdLBHBNxtOIytwLstXt5E9PddnZb0GaMcqsvDBpg=\nk8s.io/apiextensions-apiserver v0.23.0 h1:uii8BYmHYiT2ZTAJxmvc3X8UhNYMxl2A0z0Xq3Pm+WY=\nk8s.io/apiextensions-apiserver v0.23.0/go.mod h1:xIFAEEDlAZgpVBl/1VSjGDmLoXAWRG40+GsWhKhAxY4=\nk8s.io/apimachinery v0.23.0 h1:mIfWRMjBuMdolAWJ3Fd+aPTMv3X9z+waiARMpvvb0HQ=\nk8s.io/apimachinery v0.23.0/go.mod h1:fFCTTBKvKcwTPFzjlcxp91uPFZr+JA0FubU4fLzzFYc=\nk8s.io/apiserver v0.23.0 h1:Ds/QveXWi9aJ8ISB0CJa4zBNc5njxAs5u3rmMIexqCY=\nk8s.io/apiserver v0.23.0/go.mod h1:Cec35u/9zAepDPPFyT+UMrgqOCjgJ5qtfVJDxjZYmt4=\nk8s.io/cli-runtime v0.23.0/go.mod h1:B5N3YH0KP1iKr6gEuJ/RRmGjO0mJQ/f/JrsmEiPQAlU=\nk8s.io/client-go v0.23.0 h1:vcsOqyPq7XV3QmQRCBH/t9BICJM9Q1M18qahjv+rebY=\nk8s.io/client-go v0.23.0/go.mod h1:hrDnpnK1mSr65lHHcUuIZIXDgEbzc7/683c6hyG4jTA=\nk8s.io/cluster-bootstrap v0.23.0 h1:8pZuuAWPoygewSNB4IddX3HBwXcQkPDXL/ca7GtGf4o=\nk8s.io/cluster-bootstrap v0.23.0/go.mod h1:VltEnKWfrRTiKgOXp3ts3vh7yqNlH6KFKFflo9GtCBg=\nk8s.io/code-generator v0.23.0/go.mod h1:vQvOhDXhuzqiVfM/YHp+dmg10WDZCchJVObc9MvowsE=\nk8s.io/component-base v0.23.0 h1:UAnyzjvVZ2ZR1lF35YwtNY6VMN94WtOnArcXBu34es8=\nk8s.io/component-base v0.23.0/go.mod h1:DHH5uiFvLC1edCpvcTDV++NKULdYYU6pR9Tt3HIKMKI=\nk8s.io/component-helpers v0.23.0/go.mod h1:liXMh6FZS4qamKtMJQ7uLHnFe3tlC86RX5mJEk/aerg=\nk8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=\nk8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=\nk8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=\nk8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=\nk8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw=\nk8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=\nk8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=\nk8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 h1:E3J9oCLlaobFUqsjG9DfKbP2BmgwBL2p7pn0A3dG9W4=\nk8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk=\nk8s.io/kubectl v0.23.0/go.mod h1:TfcGEs3u4dkmoC2eku1GYymdGaMtPMcaLLFrX/RB2kI=\nk8s.io/metrics v0.23.0/go.mod h1:NDiZTwppEtAuKJ1Rxt3S4dhyRzdp6yUcJf0vo023dPo=\nk8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=\nk8s.io/utils v0.0.0-20210930125809-cb0fa318a74b h1:wxEMGetGMur3J1xuGLQY7GEQYg9bZxKn3tKo5k/eYcs=\nk8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\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/apiserver-network-proxy/konnectivity-client v0.0.25/go.mod h1:Mlj9PNLmG9bZ6BHFwFKDo5afkpWyUISkb9Me0GnK66I=\nsigs.k8s.io/cluster-api v1.1.2 h1:v00hk4crISOo2sKBhvOq0PC375BPk79Cpflt3Jtn7k8=\nsigs.k8s.io/cluster-api v1.1.2/go.mod h1:aq0b2tkMHZDTnuLEU7KOZOiQ5Pg82s3vh/KH/X6c/mM=\nsigs.k8s.io/controller-runtime v0.11.1 h1:7YIHT2QnHJArj/dk9aUkYhfqfK5cIxPOX5gPECfdZLU=\nsigs.k8s.io/controller-runtime v0.11.1/go.mod h1:KKwLiTooNGu+JmLZGn9Sl3Gjmfj66eMbCQznLP5zcqA=\nsigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s=\nsigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs=\nsigs.k8s.io/kustomize/api v0.10.1/go.mod h1:2FigT1QN6xKdcnGS2Ppp1uIWrtWN28Ms8A3OZUZhwr8=\nsigs.k8s.io/kustomize/cmd/config v0.10.2/go.mod h1:K2aW7nXJ0AaT+VA/eO0/dzFLxmpFcTzudmAgDwPY1HQ=\nsigs.k8s.io/kustomize/kustomize/v4 v4.4.1/go.mod h1:qOKJMMz2mBP+vcS7vK+mNz4HBLjaQSWRY22EF6Tb7Io=\nsigs.k8s.io/kustomize/kyaml v0.13.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=\nsigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=\nsigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=\nsigs.k8s.io/structured-merge-diff/v4 v4.2.0 h1:kDvPBbnPk+qYmkHmSo8vKGp438IASWofnbbUKDE/bv0=\nsigs.k8s.io/structured-merge-diff/v4 v4.2.0/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=\nsigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=\nsigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=\nsigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=\n"
  },
  {
    "path": "chapter/14/petstore-operator/hack/boilerplate.go.txt",
    "content": "/*\nCopyright 2022.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/"
  },
  {
    "path": "chapter/14/petstore-operator/main.go",
    "content": "/*\nCopyright 2022.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage main\n\nimport (\n\t\"flag\"\n\t\"os\"\n\n\t// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)\n\t// to ensure that exec-entrypoint and run can make use of them.\n\t_ \"k8s.io/client-go/plugin/pkg/client/auth\"\n\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\tutilruntime \"k8s.io/apimachinery/pkg/util/runtime\"\n\tclientgoscheme \"k8s.io/client-go/kubernetes/scheme\"\n\tctrl \"sigs.k8s.io/controller-runtime\"\n\t\"sigs.k8s.io/controller-runtime/pkg/healthz\"\n\t\"sigs.k8s.io/controller-runtime/pkg/log/zap\"\n\n\tpetstorev1alpha1 \"github.com/PacktPublishing/Go-for-DevOps/chapter/14/petstore-operator/api/v1alpha1\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/14/petstore-operator/controllers\"\n\t//+kubebuilder:scaffold:imports\n)\n\nvar (\n\tscheme   = runtime.NewScheme()\n\tsetupLog = ctrl.Log.WithName(\"setup\")\n)\n\nfunc init() {\n\tutilruntime.Must(clientgoscheme.AddToScheme(scheme))\n\n\tutilruntime.Must(petstorev1alpha1.AddToScheme(scheme))\n\t//+kubebuilder:scaffold:scheme\n}\n\nfunc main() {\n\tvar metricsAddr string\n\tvar enableLeaderElection bool\n\tvar probeAddr string\n\tflag.StringVar(&metricsAddr, \"metrics-bind-address\", \":8080\", \"The address the metric endpoint binds to.\")\n\tflag.StringVar(&probeAddr, \"health-probe-bind-address\", \":8081\", \"The address the probe endpoint binds to.\")\n\tflag.BoolVar(&enableLeaderElection, \"leader-elect\", false,\n\t\t\"Enable leader election for controller manager. \"+\n\t\t\t\"Enabling this will ensure there is only one active controller manager.\")\n\topts := zap.Options{\n\t\tDevelopment: true,\n\t}\n\topts.BindFlags(flag.CommandLine)\n\tflag.Parse()\n\n\tctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))\n\n\tmgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{\n\t\tScheme:                 scheme,\n\t\tMetricsBindAddress:     metricsAddr,\n\t\tPort:                   9443,\n\t\tHealthProbeBindAddress: probeAddr,\n\t\tLeaderElection:         enableLeaderElection,\n\t\tLeaderElectionID:       \"9c3fedb1.example.com\",\n\t})\n\tif err != nil {\n\t\tsetupLog.Error(err, \"unable to start manager\")\n\t\tos.Exit(1)\n\t}\n\n\tif err = (&controllers.PetReconciler{\n\t\tClient: mgr.GetClient(),\n\t\tScheme: mgr.GetScheme(),\n\t}).SetupWithManager(mgr); err != nil {\n\t\tsetupLog.Error(err, \"unable to create controller\", \"controller\", \"Pet\")\n\t\tos.Exit(1)\n\t}\n\t//+kubebuilder:scaffold:builder\n\n\tif err := mgr.AddHealthzCheck(\"healthz\", healthz.Ping); err != nil {\n\t\tsetupLog.Error(err, \"unable to set up health check\")\n\t\tos.Exit(1)\n\t}\n\tif err := mgr.AddReadyzCheck(\"readyz\", healthz.Ping); err != nil {\n\t\tsetupLog.Error(err, \"unable to set up ready check\")\n\t\tos.Exit(1)\n\t}\n\n\tsetupLog.Info(\"starting manager\")\n\tif err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {\n\t\tsetupLog.Error(err, \"problem running manager\")\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "chapter/14/workloads/Readme.md",
    "content": "# Interacting with Kubernetes Workloads\nIn this example, we'll explore how to interact with the Kubernetes API using a Go client to create a namespace, ingress, service, and deployment to run an Nginx hello world service.\n\nThe service runs locally in KinD streaming logs from the pods in the service to STDOUT. After the service is started, open a browser to http://localhost:8080/hello. You should see the request stream to STDOUT, and you should be greeted with a page describing the request and the server that served it. If you refresh the page, you should see the server name change indicating the requests are load balancing across the 2 pod replicas in the deployment.\n\n## Required tools\n- docker\n- kubectl\n- kind\n\n## Running the code\n```shell\nkind create cluster --name workloads --config kind-config.yaml\nkubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml\nkubectl wait --namespace ingress-nginx \\\n  --for=condition=ready pod \\\n  --selector=app.kubernetes.io/component=controller \\\n  --timeout=90s\ngo run .\n```\n\n## Deleting the KinD cluster\nDeleting the KinD cluster will clean up all resources used for this example.\n```shell\nkind delete cluster --name workloads\n```\n"
  },
  {
    "path": "chapter/14/workloads/go.mod",
    "content": "module github.com/Go-for-DevOps/chapter/14/workloads\n\ngo 1.17\n\nrequire (\n\tgithub.com/Azure/go-autorest/autorest/to v0.4.0\n\tk8s.io/api v0.23.4\n\tk8s.io/apimachinery v0.23.4\n\tk8s.io/client-go v0.23.4\n)\n\nrequire (\n\tgithub.com/Azure/go-autorest v14.2.0+incompatible // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/go-logr/logr v1.2.0 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgithub.com/google/go-cmp v0.5.5 // indirect\n\tgithub.com/google/gofuzz v1.1.0 // indirect\n\tgithub.com/googleapis/gnostic v0.5.5 // indirect\n\tgithub.com/imdario/mergo v0.3.5 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // 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/spf13/pflag v1.0.5 // indirect\n\tgolang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect\n\tgolang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect\n\tgolang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e // indirect\n\tgolang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect\n\tgolang.org/x/text v0.3.7 // indirect\n\tgolang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect\n\tgoogle.golang.org/appengine v1.6.7 // indirect\n\tgoogle.golang.org/protobuf v1.27.1 // indirect\n\tgopkg.in/inf.v0 v0.9.1 // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect\n\tk8s.io/klog/v2 v2.30.0 // indirect\n\tk8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect\n\tk8s.io/utils v0.0.0-20211116205334-6203023598ed // indirect\n\tsigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect\n\tsigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect\n\tsigs.k8s.io/yaml v1.2.0 // indirect\n)\n"
  },
  {
    "path": "chapter/14/workloads/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 v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=\ncloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=\ncloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=\ncloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=\ncloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=\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/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=\ngithub.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=\ngithub.com/Azure/go-autorest/autorest v0.11.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM=\ngithub.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=\ngithub.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=\ngithub.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=\ngithub.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk=\ngithub.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=\ngithub.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=\ngithub.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=\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/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=\ngithub.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=\ngithub.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\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/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-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\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/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=\ngithub.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=\ngithub.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=\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.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=\ngithub.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=\ngithub.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=\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-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=\ngithub.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=\ngithub.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE=\ngithub.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=\ngithub.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\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/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/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\ngithub.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=\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.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=\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/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=\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.2/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 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=\ngithub.com/google/gofuzz v1.1.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/martian/v3 v3.1.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/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\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/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=\ngithub.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=\ngithub.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=\ngithub.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=\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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=\ngithub.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\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/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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.0/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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=\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 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/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=\ngithub.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=\ngithub.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=\ngithub.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\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 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\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.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.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=\ngo.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=\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-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-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/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/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\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-20201208152925-83fdc39ff7b5/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.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.1/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-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-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-20190827160401-ba9fcec4b297/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-20201031054903-ff519b6c9102/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-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY=\ngolang.org/x/net v0.0.0-20211209124913-491a49abca63/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-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw=\ngolang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/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/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190215142949-d0b11bdaac8a/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-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-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-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-20200113162924-86b910548bc1/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-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/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-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e h1:XMgFehsDnnLGtjvjOfqWSUzt0alpTR1RSEuznObga2c=\ngolang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=\ngolang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\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.4/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-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=\ngolang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\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-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-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-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-20200505023115-26f46d2f7ef8/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-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=\ngolang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=\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=\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/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=\ngoogle.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=\ngoogle.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=\ngoogle.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=\ngoogle.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=\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/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=\ngoogle.golang.org/appengine v1.6.7/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-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-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=\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.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=\ngoogle.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=\ngoogle.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\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/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 h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=\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/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=\ngopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\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.4/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-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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=\nk8s.io/api v0.23.4 h1:85gnfXQOWbJa1SiWGpE9EEtHs0UVvDyIsSMpEtl2D4E=\nk8s.io/api v0.23.4/go.mod h1:i77F4JfyNNrhOjZF7OwwNJS5Y1S9dpwvb9iYRYRczfI=\nk8s.io/apimachinery v0.23.4 h1:fhnuMd/xUL3Cjfl64j5ULKZ1/J9n8NuQEgNL+WXWfdM=\nk8s.io/apimachinery v0.23.4/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM=\nk8s.io/client-go v0.23.4 h1:YVWvPeerA2gpUudLelvsolzH7c2sFoXXR5wM/sWqNFU=\nk8s.io/client-go v0.23.4/go.mod h1:PKnIL4pqLuvYUK1WU7RLTMYKPiIh7MYShLshtRY9cj0=\nk8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=\nk8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=\nk8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=\nk8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw=\nk8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=\nk8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 h1:E3J9oCLlaobFUqsjG9DfKbP2BmgwBL2p7pn0A3dG9W4=\nk8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk=\nk8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=\nk8s.io/utils v0.0.0-20211116205334-6203023598ed h1:ck1fRPWPJWsMd8ZRFsWc6mh/zHp5fZ/shhbrgPUxDAE=\nk8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\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/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s=\nsigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs=\nsigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=\nsigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y=\nsigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=\nsigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=\nsigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=\n"
  },
  {
    "path": "chapter/14/workloads/kind-config.yaml",
    "content": "kind: Cluster\napiVersion: kind.x-k8s.io/v1alpha4\nnodes:\n  - role: control-plane\n    kubeadmConfigPatches:\n      - |\n        kind: InitConfiguration\n        nodeRegistration:\n          kubeletExtraArgs:\n            node-labels: \"ingress-ready=true\"\n    extraPortMappings:\n      - containerPort: 80\n        hostPort: 8080\n        protocol: TCP\n      - containerPort: 443\n        hostPort: 8081\n        protocol: TCP\n"
  },
  {
    "path": "chapter/14/workloads/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/signal\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/Azure/go-autorest/autorest/to\"\n\tappv1 \"k8s.io/api/apps/v1\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tnetv1 \"k8s.io/api/networking/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/client-go/kubernetes\"\n\t\"k8s.io/client-go/tools/clientcmd\"\n\t\"k8s.io/client-go/util/homedir\"\n)\n\nfunc main() {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tclientSet := getClientSet()\n\n\t// create a namespace named \"foo\" and delete it when main exits\n\tnsFoo := createNamespace(ctx, clientSet, \"foo\")\n\tdefer func() {\n\t\tdeleteNamespace(ctx, clientSet, nsFoo)\n\t}()\n\n\t// create an nginx deployment named \"hello-world\" in the nsFoo namespace\n\tdeployNginx(ctx, clientSet, nsFoo, \"hello-world\")\n\tfmt.Printf(\"You can now see your running service: http://localhost:8080/hello\\n\\n\")\n\n\t// listen to pod logs from namespace foo\n\tlistenToPodLogs(ctx, clientSet, nsFoo, \"hello-world\")\n\n\t// wait for ctrl-c to exit the program\n\twaitForExitSignal()\n}\n\nfunc createNamespace(ctx context.Context, clientSet *kubernetes.Clientset, name string) *corev1.Namespace {\n\tfmt.Printf(\"Creating namespace %q.\\n\\n\", name)\n\tns := &corev1.Namespace{\n\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\tName: name,\n\t\t},\n\t}\n\tns, err := clientSet.CoreV1().Namespaces().Create(ctx, ns, metav1.CreateOptions{})\n\tpanicIfError(err)\n\treturn ns\n}\n\nfunc deleteNamespace(ctx context.Context, clientSet *kubernetes.Clientset, ns *corev1.Namespace) {\n\tfmt.Printf(\"\\n\\nDeleting namespace %q.\\n\", ns.Name)\n\tpanicIfError(clientSet.CoreV1().Namespaces().Delete(ctx, ns.Name, metav1.DeleteOptions{}))\n}\n\nfunc deployNginx(ctx context.Context, clientSet *kubernetes.Clientset, ns *corev1.Namespace, name string) {\n\tdeployment := createNginxDeployment(ctx, clientSet, ns, name)\n\twaitForReadyReplicas(ctx, clientSet, deployment)\n\tcreateNginxService(ctx, clientSet, ns, name)\n\tcreateNginxIngress(ctx, clientSet, ns, name)\n}\n\nfunc createNginxDeployment(ctx context.Context, clientSet *kubernetes.Clientset, ns *corev1.Namespace, name string) *appv1.Deployment {\n\tvar (\n\t\tmatchLabel = map[string]string{\"app\": \"nginx\"}\n\t\tobjMeta    = metav1.ObjectMeta{\n\t\t\tName:      name,\n\t\t\tNamespace: ns.Name,\n\t\t\tLabels:    matchLabel,\n\t\t}\n\t)\n\n\tdeployment := &appv1.Deployment{\n\t\tObjectMeta: objMeta,\n\t\tSpec: appv1.DeploymentSpec{\n\t\t\tReplicas: to.Int32Ptr(2),\n\t\t\tSelector: &metav1.LabelSelector{MatchLabels: matchLabel},\n\t\t\tTemplate: corev1.PodTemplateSpec{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tLabels: matchLabel,\n\t\t\t\t},\n\t\t\t\tSpec: corev1.PodSpec{\n\t\t\t\t\tContainers: []corev1.Container{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName:  name,\n\t\t\t\t\t\t\tImage: \"nginxdemos/hello:latest\",\n\t\t\t\t\t\t\tPorts: []corev1.ContainerPort{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tContainerPort: 80,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\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},\n\t}\n\tdeployment, err := clientSet.AppsV1().Deployments(ns.Name).Create(ctx, deployment, metav1.CreateOptions{})\n\tpanicIfError(err)\n\treturn deployment\n}\n\nfunc waitForReadyReplicas(ctx context.Context, clientSet *kubernetes.Clientset, deployment *appv1.Deployment) {\n\tfmt.Printf(\"Waiting for ready replicas in deployment %q\\n\", deployment.Name)\n\tfor {\n\t\texpectedReplicas := *deployment.Spec.Replicas\n\t\treadyReplicas := getReadyReplicasForDeployment(ctx, clientSet, deployment)\n\t\tif readyReplicas == expectedReplicas {\n\t\t\tfmt.Printf(\"replicas are ready!\\n\\n\")\n\t\t\tbreak\n\t\t}\n\n\t\tfmt.Printf(\"replicas are not ready yet. %d/%d\\n\", readyReplicas, expectedReplicas)\n\t\ttime.Sleep(1 * time.Second)\n\t}\n}\n\nfunc getReadyReplicasForDeployment(ctx context.Context, clientSet *kubernetes.Clientset, deployment *appv1.Deployment) int32 {\n\tdep, err := clientSet.AppsV1().Deployments(deployment.Namespace).Get(ctx, deployment.Name, metav1.GetOptions{})\n\tpanicIfError(err)\n\n\treturn dep.Status.ReadyReplicas\n}\n\nfunc createNginxService(ctx context.Context, clientSet *kubernetes.Clientset, ns *corev1.Namespace, name string) {\n\tvar (\n\t\tmatchLabel = map[string]string{\"app\": \"nginx\"}\n\t\tobjMeta    = metav1.ObjectMeta{\n\t\t\tName:      name,\n\t\t\tNamespace: ns.Name,\n\t\t\tLabels:    matchLabel,\n\t\t}\n\t)\n\n\tservice := &corev1.Service{\n\t\tObjectMeta: objMeta,\n\t\tSpec: corev1.ServiceSpec{\n\t\t\tSelector: matchLabel,\n\t\t\tPorts: []corev1.ServicePort{\n\t\t\t\t{\n\t\t\t\t\tPort:     80,\n\t\t\t\t\tProtocol: corev1.ProtocolTCP,\n\t\t\t\t\tName:     \"http\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tservice, err := clientSet.CoreV1().Services(ns.Name).Create(ctx, service, metav1.CreateOptions{})\n\tpanicIfError(err)\n}\n\nfunc createNginxIngress(ctx context.Context, clientSet *kubernetes.Clientset, ns *corev1.Namespace, name string) {\n\tvar (\n\t\tprefix  = netv1.PathTypePrefix\n\t\tobjMeta = metav1.ObjectMeta{\n\t\t\tName:      name,\n\t\t\tNamespace: ns.Name,\n\t\t}\n\t\tingressPath = netv1.HTTPIngressPath{\n\t\t\tPathType: &prefix,\n\t\t\tPath:     \"/hello\",\n\t\t\tBackend: netv1.IngressBackend{\n\t\t\t\tService: &netv1.IngressServiceBackend{\n\t\t\t\t\tName: name,\n\t\t\t\t\tPort: netv1.ServiceBackendPort{\n\t\t\t\t\t\tName: \"http\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t)\n\n\tingress := &netv1.Ingress{\n\t\tObjectMeta: objMeta,\n\t\tSpec: netv1.IngressSpec{\n\t\t\tRules: []netv1.IngressRule{\n\t\t\t\t{\n\t\t\t\t\tIngressRuleValue: netv1.IngressRuleValue{\n\t\t\t\t\t\tHTTP: &netv1.HTTPIngressRuleValue{\n\t\t\t\t\t\t\tPaths: []netv1.HTTPIngressPath{ingressPath},\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},\n\t}\n\tingress, err := clientSet.NetworkingV1().Ingresses(ns.Name).Create(ctx, ingress, metav1.CreateOptions{})\n\tpanicIfError(err)\n}\n\nfunc listenToPodLogs(ctx context.Context, clientSet *kubernetes.Clientset, ns *corev1.Namespace, containerName string) {\n\t// list all the pods in namespace foo\n\tpodList := listPods(ctx, clientSet, ns)\n\n\tfor _, pod := range podList.Items {\n\t\tpodName := pod.Name\n\t\tgo func() {\n\t\t\topts := &corev1.PodLogOptions{\n\t\t\t\tContainer: containerName,\n\t\t\t\tFollow:    true,\n\t\t\t}\n\t\t\tpodLogs, err := clientSet.CoreV1().Pods(ns.Name).GetLogs(podName, opts).Stream(ctx)\n\t\t\tpanicIfError(err)\n\n\t\t\t_, _ = os.Stdout.ReadFrom(podLogs)\n\t\t}()\n\t}\n}\n\nfunc listPods(ctx context.Context, clientSet *kubernetes.Clientset, ns *corev1.Namespace) *corev1.PodList {\n\tpodList, err := clientSet.CoreV1().Pods(ns.Name).List(ctx, metav1.ListOptions{})\n\tpanicIfError(err)\n\n\tfmt.Printf(\"Listing pods in %q namespace.\\n\", ns.Name)\n\tfor _, pod := range podList.Items {\n\t\tfmt.Printf(\"# Pod: \\n## namespace/name: %q\\n## spec.containers[0].name: %q\\n## spec.containers[0].image: %q\\n\", path.Join(pod.Namespace, pod.Name), pod.Spec.Containers[0].Name, pod.Spec.Containers[0].Image)\n\t}\n\tfmt.Printf(\"\\n\\n\")\n\treturn podList\n}\n\nfunc waitForExitSignal() {\n\tfmt.Printf(\"Type ctrl-c to exit\\n\\n\")\n\tsigs := make(chan os.Signal, 1)\n\tsignal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)\n\t<-sigs\n}\n\nfunc getClientSet() *kubernetes.Clientset {\n\tvar kubeconfig *string\n\tif home := homedir.HomeDir(); home != \"\" {\n\t\tkubeconfig = flag.String(\"kubeconfig\", filepath.Join(home, \".kube\", \"config\"), \"(optional) absolute path to the kubeconfig file\")\n\t} else {\n\t\tkubeconfig = flag.String(\"kubeconfig\", \"\", \"absolute path to the kubeconfig file\")\n\t}\n\tflag.Parse()\n\n\t// use the current context in kubeconfig\n\tconfig, err := clientcmd.BuildConfigFromFlags(\"\", *kubeconfig)\n\tpanicIfError(err)\n\n\t// create the clientSet\n\tcs, err := kubernetes.NewForConfig(config)\n\tpanicIfError(err)\n\treturn cs\n}\n\nfunc panicIfError(err error) {\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n}\n"
  },
  {
    "path": "chapter/15/.gitignore",
    "content": ".env\n.ssh\nbin\n"
  },
  {
    "path": "chapter/15/cloud-init/init.yml",
    "content": "#cloud-config\npackage_upgrade: true\npackages:\n  - nginx\n  - golang\nruncmd:\n  - echo \"hello world\"\n"
  },
  {
    "path": "chapter/15/cmd/compute/main.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/joho/godotenv\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/15/pkg/helpers\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/15/pkg/mgmt\"\n)\n\nfunc init() {\n\t_ = godotenv.Load()\n}\n\nfunc main() {\n\tsubscriptionID := helpers.MustGetenv(\"AZURE_SUBSCRIPTION_ID\")\n\tsshPubKeyPath := helpers.MustGetenv(\"SSH_PUBLIC_KEY_PATH\")\n\tfactory := mgmt.NewVirtualMachineFactory(subscriptionID, sshPubKeyPath)\n\tfmt.Println(\"Staring to build Azure resources...\")\n\tstack := factory.CreateVirtualMachineStack(context.Background(), \"southcentralus\")\n\n\tvar (\n\t\tadmin           = stack.VirtualMachine.Properties.OSProfile.AdminUsername\n\t\tipAddress       = stack.PublicIP.Properties.IPAddress\n\t\tsshIdentityPath = strings.TrimRight(sshPubKeyPath, \".pub\")\n\t)\n\tfmt.Printf(\"Connect with: `ssh -i %s %s@%s`\\n\\n\", sshIdentityPath, *admin, *ipAddress)\n\tfmt.Println(\"Press enter to delete the infrastructure.\")\n\treader := bufio.NewReader(os.Stdin)\n\t_, _ = reader.ReadString('\\n')\n\tfactory.DestroyVirtualMachineStack(context.Background(), stack)\n}\n"
  },
  {
    "path": "chapter/15/cmd/storage/main.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path\"\n\t\"time\"\n\n\t\"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob\"\n\t\"github.com/joho/godotenv\"\n\n\t. \"github.com/PacktPublishing/Go-for-DevOps/chapter/15/pkg/helpers\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/15/pkg/mgmt\"\n)\n\nfunc init() {\n\t_ = godotenv.Load()\n}\n\nfunc main() {\n\tsubscriptionID := MustGetenv(\"AZURE_SUBSCRIPTION_ID\")\n\tfactory := mgmt.NewStorageFactory(subscriptionID)\n\tfmt.Println(\"Staring to build Azure resources...\")\n\tstack := factory.CreateStorageStack(context.Background(), \"southcentralus\")\n\n\tuploadBlobs(stack)\n\tprintSASUris(stack)\n\n\tfmt.Println(\"Press enter to delete the infrastructure.\")\n\treader := bufio.NewReader(os.Stdin)\n\t_, _ = reader.ReadString('\\n')\n\tfactory.DestroyStorageStack(context.Background(), stack)\n}\n\nfunc uploadBlobs(stack *mgmt.StorageStack) {\n\tserviceClient := stack.ServiceClient()\n\tcontainerClient, err := serviceClient.NewContainerClient(\"jd-imgs\")\n\tHandleErr(err)\n\n\tfmt.Printf(\"Creating a new container \\\"jd-imgs\\\" in the Storage Account...\\n\")\n\t_, err = containerClient.Create(context.Background(), nil)\n\tHandleErr(err)\n\n\tfmt.Printf(\"Reading all files ./blobs...\\n\")\n\tfiles, err := ioutil.ReadDir(\"./blobs\")\n\tHandleErr(err)\n\tfor _, file := range files {\n\t\tfmt.Printf(\"Uploading file %q to container jd-imgs...\\n\", file.Name())\n\t\tblobClient := HandleErrWithResult(containerClient.NewBlockBlobClient(file.Name()))\n\t\tosFile := HandleErrWithResult(os.Open(path.Join(\"./blobs\", file.Name())))\n\t\t_ = HandleErrWithResult(blobClient.UploadFile(context.Background(), osFile, azblob.UploadOption{}))\n\t}\n}\n\nfunc printSASUris(stack *mgmt.StorageStack) {\n\tserviceClient := stack.ServiceClient()\n\tcontainerClient, err := serviceClient.NewContainerClient(\"jd-imgs\")\n\tHandleErr(err)\n\n\tfmt.Printf(\"\\nGenerating readonly links to blobs that expire in 2 hours...\\n\")\n\tfiles := HandleErrWithResult(ioutil.ReadDir(\"./blobs\"))\n\tfor _, file := range files {\n\t\tblobClient := HandleErrWithResult(containerClient.NewBlockBlobClient(file.Name()))\n\t\tpermissions := azblob.BlobSASPermissions{\n\t\t\tRead: true,\n\t\t}\n\t\tnow := time.Now().UTC()\n\t\tsasQuery := HandleErrWithResult(blobClient.GetSASToken(permissions, now, now.Add(2*time.Hour)))\n\t\tfmt.Println(blobClient.URL() + \"?\" + sasQuery.Encode())\n\t}\n}\n"
  },
  {
    "path": "chapter/15/go.mod",
    "content": "module github.com/PacktPublishing/Go-for-DevOps/chapter/15\n\ngo 1.18\n\nrequire (\n\tgithub.com/Azure/azure-sdk-for-go/sdk/azcore v0.23.0\n\tgithub.com/Azure/azure-sdk-for-go/sdk/azidentity v0.14.0\n\tgithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v0.6.0\n\tgithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v0.4.0\n\tgithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v0.4.0\n\tgithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v0.6.0\n\tgithub.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.4.0\n\tgithub.com/joho/godotenv v1.4.0\n\tgithub.com/mitchellh/go-homedir v1.1.0\n\tgithub.com/yelinaung/go-haikunator v0.0.0-20150320004105-1249cae259af\n)\n\nrequire (\n\tgithub.com/Azure/azure-sdk-for-go/sdk/internal v0.9.2 // indirect\n\tgithub.com/AzureAD/microsoft-authentication-library-for-go v0.4.0 // indirect\n\tgithub.com/golang-jwt/jwt v3.2.1+incompatible // indirect\n\tgithub.com/google/uuid v1.1.1 // indirect\n\tgithub.com/kylelemons/godebug v1.1.0 // indirect\n\tgithub.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect\n\tgolang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 // indirect\n\tgolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect\n\tgolang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect\n\tgolang.org/x/text v0.3.7 // indirect\n)\n"
  },
  {
    "path": "chapter/15/go.sum",
    "content": "github.com/Azure/azure-sdk-for-go/sdk/azcore v0.23.0 h1:D7l5jspkc4kwBYRWoZE4DQnu6LVpLwDsMZjBKS4wZLQ=\ngithub.com/Azure/azure-sdk-for-go/sdk/azcore v0.23.0/go.mod h1:w5pDIZuawUmY3Bj4tVx3Xb8KS96ToB0j315w9rqpAg0=\ngithub.com/Azure/azure-sdk-for-go/sdk/azidentity v0.14.0 h1:NVS/4LOQfkBpk+B1VopIzv1ptmYeEskA8w/3K/w7vjo=\ngithub.com/Azure/azure-sdk-for-go/sdk/azidentity v0.14.0/go.mod h1:RG0cZndeZM17StwohYclmcXSr4oOJ8b1I5hB8llIc6Y=\ngithub.com/Azure/azure-sdk-for-go/sdk/internal v0.9.2 h1:Px2KVERcYEg2Lv25AqC2hVr0xUWaq94wuEObLIkYzmA=\ngithub.com/Azure/azure-sdk-for-go/sdk/internal v0.9.2/go.mod h1:CdSJQNNzZhCkwDaV27XV1w48ZBPtxe7mlrZAsPNxD5g=\ngithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v0.6.0 h1:p65zCzT+l0eeC94l+Ax1cMsZyrLKzEN0sNesJKEUeAA=\ngithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v0.6.0/go.mod h1:F1Y6OEs1Q0IoRrlkw9loA1PZ/wnCKNb3kPLkZ4Uqk+s=\ngithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v0.3.0 h1:gYG7I0WxtCnHSWOFLbWCHLBWr+yzI5UeU9KuGZajf5U=\ngithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v0.4.0 h1:VNxCIK7KSqUWIRd8f+0NmDfwcLesigWKNffSMJ6uYIo=\ngithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v0.4.0/go.mod h1:AzPq7EbLxPd3Q2v82xnS3ngMYjcEjoAQqfER9JYkbzg=\ngithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v0.4.0 h1:TSBK+EXu33WBNrYhWmOecKJAl4z1Z5dtbDmqOzd1zGk=\ngithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v0.4.0/go.mod h1:tt77DwGu+r0Ued27YQPhiW8h8YWpYwpfOfi5uRpRMTg=\ngithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v0.6.0 h1:ukJ+nvDsWdEWCrlvR3xDJDiHLcTuxHuX7/gErD33T70=\ngithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v0.6.0/go.mod h1:flXFQOQ2GUYDHAMwynF6VeCmy5b+nnc/jd5J0njl13o=\ngithub.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.4.0 h1:0nJeKDmB7a1a8RDMjTltahlPsaNlWjq/LpkZleSwINk=\ngithub.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.4.0/go.mod h1:mbwxKc/fW+IkF0GG591MuXw0KuEQBDkeRoZ9vmVJPxg=\ngithub.com/AzureAD/microsoft-authentication-library-for-go v0.4.0 h1:WVsrXCnHlDDX8ls+tootqRE87/hL9S/g4ewig9RsD/c=\ngithub.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=\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/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c=\ngithub.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=\ngithub.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=\ngithub.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=\ngithub.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=\ngithub.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=\ngithub.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=\ngithub.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=\ngithub.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=\ngithub.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=\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/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=\ngithub.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=\ngithub.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI=\ngithub.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=\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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=\ngithub.com/yelinaung/go-haikunator v0.0.0-20150320004105-1249cae259af h1:q8vvhFu/wCz94XJxzF0hCzfNxIb6E+dTRIsh9vsWAsI=\ngithub.com/yelinaung/go-haikunator v0.0.0-20150320004105-1249cae259af/go.mod h1:jGDZu6LyiOPbvJqHW0320zIqCODGq8zYdVS0ZE6Jlso=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=\ngolang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.2.1/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 h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=\n"
  },
  {
    "path": "chapter/15/pkg/helpers/helpers.go",
    "content": "package helpers\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/Azure/azure-sdk-for-go/sdk/azcore\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm\"\n\tarmruntime \"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/azidentity\"\n)\n\nconst (\n\tDefaultPollingFreq = 10 * time.Second\n)\n\ntype ClientBuilderFunc[T any] func(string, azcore.TokenCredential, *arm.ClientOptions) (*T, error)\n\nfunc MustGetenv(key string) string {\n\tval := os.Getenv(key)\n\tif val == \"\" {\n\t\tlog.Fatalf(\"please add your %s to the .env file\", key)\n\t}\n\treturn val\n}\n\nfunc BuildClient[T any](subID string, cred *azidentity.DefaultAzureCredential, builderFunc ClientBuilderFunc[T]) *T {\n\treturn HandleErrWithResult(builderFunc(subID, cred, nil))\n}\n\nfunc HandleErrPoller[T any](ctx context.Context, poller *armruntime.Poller[T]) T {\n\tres, err := poller.PollUntilDone(ctx, DefaultPollingFreq)\n\tHandleErr(err)\n\treturn res\n}\n\nfunc HandleErrWithResult[T any](result T, err error) T {\n\tHandleErr(err)\n\treturn result\n}\n\nfunc HandleErr(err error) {\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n"
  },
  {
    "path": "chapter/15/pkg/mgmt/compute.go",
    "content": "package mgmt\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"time\"\n\n\t\"github.com/Azure/azure-sdk-for-go/sdk/azcore\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/azcore/to\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/azidentity\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources\"\n\t\"github.com/mitchellh/go-homedir\"\n\t\"github.com/yelinaung/go-haikunator\"\n\n\t. \"github.com/PacktPublishing/Go-for-DevOps/chapter/15/pkg/helpers\"\n)\n\nvar (\n\thaiku = haikunator.New(time.Now().UnixNano())\n)\n\ntype VirtualMachineFactory struct {\n\tsubscriptionID string\n\tsshPubKeyPath  string\n\tcred           azcore.TokenCredential\n\tgroupsClient   *armresources.ResourceGroupsClient\n\tvmClient       *armcompute.VirtualMachinesClient\n\tvnetClient     *armnetwork.VirtualNetworksClient\n\tsubnetClient   *armnetwork.SubnetsClient\n\tnicClient      *armnetwork.InterfacesClient\n\tnsgClient      *armnetwork.SecurityGroupsClient\n\tpipClient      *armnetwork.PublicIPAddressesClient\n}\n\n// NewVirtualMachineFactory instantiates an Azure VirtualMachine factory\nfunc NewVirtualMachineFactory(subscriptionID, sshPubKeyPath string) *VirtualMachineFactory {\n\tcred := HandleErrWithResult(azidentity.NewDefaultAzureCredential(nil))\n\treturn &VirtualMachineFactory{\n\t\tcred:           cred,\n\t\tsubscriptionID: subscriptionID,\n\t\tsshPubKeyPath:  sshPubKeyPath,\n\t\tgroupsClient:   BuildClient(subscriptionID, cred, armresources.NewResourceGroupsClient),\n\t\tvmClient:       BuildClient(subscriptionID, cred, armcompute.NewVirtualMachinesClient),\n\t\tvnetClient:     BuildClient(subscriptionID, cred, armnetwork.NewVirtualNetworksClient),\n\t\tsubnetClient:   BuildClient(subscriptionID, cred, armnetwork.NewSubnetsClient),\n\t\tnsgClient:      BuildClient(subscriptionID, cred, armnetwork.NewSecurityGroupsClient),\n\t\tnicClient:      BuildClient(subscriptionID, cred, armnetwork.NewInterfacesClient),\n\t\tpipClient:      BuildClient(subscriptionID, cred, armnetwork.NewPublicIPAddressesClient),\n\t}\n}\n\ntype VirtualMachineStack struct {\n\tLocation         string\n\tsshKeyPath       string\n\tname             string\n\tResourceGroup    armresources.ResourceGroup\n\tVirtualNetwork   armnetwork.VirtualNetwork\n\tSecurityGroup    armnetwork.SecurityGroup\n\tVirtualMachine   armcompute.VirtualMachine\n\tNetworkInterface armnetwork.Interface\n\tPublicIP         armnetwork.PublicIPAddress\n}\n\n// CreateVirtualMachineStack creates a virtual machine and networking within a resource group\nfunc (vmf *VirtualMachineFactory) CreateVirtualMachineStack(ctx context.Context, location string) *VirtualMachineStack {\n\tstack := &VirtualMachineStack{\n\t\tLocation:   location,\n\t\tname:       haiku.Haikunate(),\n\t\tsshKeyPath: HandleErrWithResult(homedir.Expand(vmf.sshPubKeyPath)),\n\t}\n\n\tstack.ResourceGroup = vmf.createResourceGroup(ctx, stack.name, stack.Location)\n\tstack.SecurityGroup = vmf.createSecurityGroup(ctx, stack.name, stack.Location)\n\tstack.VirtualNetwork = vmf.createVirtualNetwork(ctx, stack)\n\tstack.VirtualMachine = vmf.createVirtualMachine(ctx, stack)\n\tstack.NetworkInterface = vmf.getFirstNetworkInterface(ctx, stack)\n\tstack.PublicIP = vmf.getPublicIPAddress(ctx, stack)\n\treturn stack\n}\n\n// DestroyVirtualMachineStack deletes a virtual machine and networking within a resource group.\n// This function does not wait for completion. Once the delete operation is accepted, the function returns.\nfunc (vmf *VirtualMachineFactory) DestroyVirtualMachineStack(ctx context.Context, vmStack *VirtualMachineStack) {\n\t_, err := vmf.groupsClient.BeginDelete(ctx, vmStack.name, nil)\n\tHandleErr(err)\n}\n\n// createResourceGroup creates an Azure resource by name and in a given location\nfunc (vmf *VirtualMachineFactory) createResourceGroup(ctx context.Context, name, location string) armresources.ResourceGroup {\n\tparam := armresources.ResourceGroup{\n\t\tLocation: to.Ptr(location),\n\t}\n\n\tfmt.Printf(\"Building an Azure Resource Group named %q...\\n\", name)\n\tres, err := vmf.groupsClient.CreateOrUpdate(ctx, name, param, nil)\n\tHandleErr(err)\n\treturn res.ResourceGroup\n}\n\n// createVirtualNetwork creates an Azure Virtual Network with a 10.0.0.0/16 CIDR with a 10.0.0.0/24 subnet\nfunc (vmf *VirtualMachineFactory) createVirtualNetwork(ctx context.Context, vmStack *VirtualMachineStack) armnetwork.VirtualNetwork {\n\tparam := armnetwork.VirtualNetwork{\n\t\tLocation: to.Ptr(vmStack.Location),\n\t\tName:     to.Ptr(vmStack.name + \"-vnet\"),\n\t\tProperties: &armnetwork.VirtualNetworkPropertiesFormat{\n\t\t\tAddressSpace: &armnetwork.AddressSpace{\n\t\t\t\tAddressPrefixes: []*string{to.Ptr(\"10.0.0.0/16\")},\n\t\t\t},\n\t\t\tSubnets: []*armnetwork.Subnet{\n\t\t\t\t{\n\t\t\t\t\tName: to.Ptr(\"subnet1\"),\n\t\t\t\t\tProperties: &armnetwork.SubnetPropertiesFormat{\n\t\t\t\t\t\tAddressPrefix:        to.Ptr(\"10.0.0.0/24\"),\n\t\t\t\t\t\tNetworkSecurityGroup: &vmStack.SecurityGroup,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfmt.Printf(\"Building an Azure Virtual Network named %q...\\n\", *param.Name)\n\tpoller, err := vmf.vnetClient.BeginCreateOrUpdate(ctx, vmStack.name, *param.Name, param, nil)\n\tHandleErr(err)\n\tres := HandleErrPoller(ctx, poller)\n\treturn res.VirtualNetwork\n}\n\n// createSecurityGroup creates an Azure Network Security Group to allow SSH on port 22\nfunc (vmf *VirtualMachineFactory) createSecurityGroup(ctx context.Context, name, location string) armnetwork.SecurityGroup {\n\tparam := armnetwork.SecurityGroup{\n\t\tLocation: to.Ptr(location),\n\t\tName:     to.Ptr(name + \"-nsg\"),\n\t\tProperties: &armnetwork.SecurityGroupPropertiesFormat{\n\t\t\tSecurityRules: []*armnetwork.SecurityRule{\n\t\t\t\t{\n\t\t\t\t\tName: to.Ptr(\"ssh\"),\n\t\t\t\t\tProperties: &armnetwork.SecurityRulePropertiesFormat{\n\t\t\t\t\t\tAccess:                   to.Ptr(armnetwork.SecurityRuleAccessAllow),\n\t\t\t\t\t\tDirection:                to.Ptr(armnetwork.SecurityRuleDirectionInbound),\n\t\t\t\t\t\tProtocol:                 to.Ptr(armnetwork.SecurityRuleProtocolAsterisk),\n\t\t\t\t\t\tDescription:              to.Ptr(\"allow ssh on 22\"),\n\t\t\t\t\t\tDestinationAddressPrefix: to.Ptr(\"*\"),\n\t\t\t\t\t\tDestinationPortRange:     to.Ptr(\"22\"),\n\t\t\t\t\t\tPriority:                 to.Ptr(int32(101)),\n\t\t\t\t\t\tSourcePortRange:          to.Ptr(\"*\"),\n\t\t\t\t\t\tSourceAddressPrefix:      to.Ptr(\"*\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfmt.Printf(\"Building an Azure Network Security Group named %q...\\n\", *param.Name)\n\tpoller, err := vmf.nsgClient.BeginCreateOrUpdate(ctx, name, *param.Name, param, nil)\n\tHandleErr(err)\n\tres := HandleErrPoller(ctx, poller)\n\treturn res.SecurityGroup\n}\n\n// createVirtualMachine creates an Azure Virtual Machine\nfunc (vmf *VirtualMachineFactory) createVirtualMachine(ctx context.Context, vmStack *VirtualMachineStack) armcompute.VirtualMachine {\n\tparam := linuxVM(vmStack)\n\n\tfmt.Printf(\"Building an Azure Virtual Machine named %q...\\n\", *param.Name)\n\tpoller, err := vmf.vmClient.BeginCreateOrUpdate(ctx, vmStack.name, *param.Name, param, nil)\n\tHandleErr(err)\n\tres := HandleErrPoller(ctx, poller)\n\treturn res.VirtualMachine\n}\n\n// getFirstNetworkInterface returns the first network interface on the vmStack Virtual Machine\nfunc (vmf *VirtualMachineFactory) getFirstNetworkInterface(ctx context.Context, vmStack *VirtualMachineStack) armnetwork.Interface {\n\tiface := vmStack.VirtualMachine.Properties.NetworkProfile.NetworkInterfaces[0]\n\tparsed := HandleErrWithResult(arm.ParseResourceID(*iface.ID))\n\tfmt.Printf(\"Fetching the first Network Interface named %q connected to the VM...\\n\", parsed.Name)\n\tres := HandleErrWithResult(vmf.nicClient.Get(ctx, vmStack.name, parsed.Name, nil))\n\treturn res.Interface\n}\n\n// getFirstNetworkInterface returns the first network interface on the vmStack Virtual Machine\nfunc (vmf *VirtualMachineFactory) getPublicIPAddress(ctx context.Context, vmStack *VirtualMachineStack) armnetwork.PublicIPAddress {\n\tpipName := vmStack.NetworkInterface.Properties.IPConfigurations[0].Properties.PublicIPAddress.Name\n\tfmt.Printf(\"Fetching the Public IP Address named %q connected to the VM...\\n\", *pipName)\n\tres := HandleErrWithResult(vmf.pipClient.Get(ctx, vmStack.name, *pipName, nil))\n\treturn res.PublicIPAddress\n}\n\n// linuxVM builds a Linux Virtual Machine structure\nfunc linuxVM(vmStack *VirtualMachineStack) armcompute.VirtualMachine {\n\treturn armcompute.VirtualMachine{\n\t\tLocation: to.Ptr(vmStack.Location),\n\t\tName:     to.Ptr(vmStack.name + \"-vm\"),\n\t\tProperties: &armcompute.VirtualMachineProperties{\n\t\t\tHardwareProfile: &armcompute.HardwareProfile{\n\t\t\t\tVMSize: to.Ptr(armcompute.VirtualMachineSizeTypesStandardD2SV3),\n\t\t\t},\n\t\t\tNetworkProfile: networkProfile(vmStack),\n\t\t\tOSProfile:      linuxOSProfile(vmStack),\n\t\t\tStorageProfile: &armcompute.StorageProfile{\n\t\t\t\tImageReference: &armcompute.ImageReference{\n\t\t\t\t\tPublisher: to.Ptr(\"Canonical\"),\n\t\t\t\t\tOffer:     to.Ptr(\"UbuntuServer\"),\n\t\t\t\t\tSKU:       to.Ptr(\"18.04-LTS\"),\n\t\t\t\t\tVersion:   to.Ptr(\"latest\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n}\n\n// networkProfile builds a Virtual Machine network profile requesting a public IP\nfunc networkProfile(vmStack *VirtualMachineStack) *armcompute.NetworkProfile {\n\tfirstSubnet := vmStack.VirtualNetwork.Properties.Subnets[0]\n\treturn &armcompute.NetworkProfile{\n\t\tNetworkAPIVersion: to.Ptr(armcompute.NetworkAPIVersionTwoThousandTwenty1101),\n\t\tNetworkInterfaceConfigurations: []*armcompute.VirtualMachineNetworkInterfaceConfiguration{\n\t\t\t{\n\t\t\t\tName: to.Ptr(vmStack.name + \"-nic\"),\n\t\t\t\tProperties: &armcompute.VirtualMachineNetworkInterfaceConfigurationProperties{\n\t\t\t\t\tIPConfigurations: []*armcompute.VirtualMachineNetworkInterfaceIPConfiguration{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName: to.Ptr(vmStack.name + \"-nic-conf\"),\n\t\t\t\t\t\t\tProperties: &armcompute.VirtualMachineNetworkInterfaceIPConfigurationProperties{\n\t\t\t\t\t\t\t\tPrimary: to.Ptr(true),\n\t\t\t\t\t\t\t\tSubnet: &armcompute.SubResource{\n\t\t\t\t\t\t\t\t\tID: firstSubnet.ID,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tPublicIPAddressConfiguration: &armcompute.VirtualMachinePublicIPAddressConfiguration{\n\t\t\t\t\t\t\t\t\tName: to.Ptr(vmStack.name + \"-pip\"),\n\t\t\t\t\t\t\t\t\tProperties: &armcompute.VirtualMachinePublicIPAddressConfigurationProperties{\n\t\t\t\t\t\t\t\t\t\tPublicIPAllocationMethod: to.Ptr(armcompute.PublicIPAllocationMethodStatic),\n\t\t\t\t\t\t\t\t\t\tPublicIPAddressVersion:   to.Ptr(armcompute.IPVersionsIPv4),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tPrimary: to.Ptr(true),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n}\n\n// linuxOSProfile creates an Azure VM OS profile with only SSH access for the devops admin user\nfunc linuxOSProfile(vmStack *VirtualMachineStack) *armcompute.OSProfile {\n\tsshKeyData := HandleErrWithResult(ioutil.ReadFile(vmStack.sshKeyPath))\n\tcloudInitContent := HandleErrWithResult(ioutil.ReadFile(\"./cloud-init/init.yml\"))\n\tb64EncodedInitScript := base64.StdEncoding.EncodeToString(cloudInitContent)\n\treturn &armcompute.OSProfile{\n\t\tAdminUsername: to.Ptr(\"devops\"),\n\t\tComputerName:  to.Ptr(vmStack.name),\n\t\tCustomData:    to.Ptr(b64EncodedInitScript),\n\t\tLinuxConfiguration: &armcompute.LinuxConfiguration{\n\t\t\tDisablePasswordAuthentication: to.Ptr(true),\n\t\t\tSSH: &armcompute.SSHConfiguration{\n\t\t\t\tPublicKeys: []*armcompute.SSHPublicKey{\n\t\t\t\t\t{\n\t\t\t\t\t\tPath:    to.Ptr(\"/home/devops/.ssh/authorized_keys\"),\n\t\t\t\t\t\tKeyData: to.Ptr(string(sshKeyData)),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "chapter/15/pkg/mgmt/storage.go",
    "content": "package mgmt\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/Azure/azure-sdk-for-go/sdk/azcore\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/azcore/to\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/azidentity\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob\"\n\n\t. \"github.com/PacktPublishing/Go-for-DevOps/chapter/15/pkg/helpers\"\n)\n\ntype StorageStack struct {\n\tLocation      string\n\tname          string\n\tCred          azcore.TokenCredential\n\tResourceGroup armresources.ResourceGroup\n\tAccount       armstorage.Account\n\tAccountKey    *armstorage.AccountKey\n}\n\ntype StorageFactory struct {\n\tsubscriptionID string\n\tcred           azcore.TokenCredential\n\tgroupsClient   *armresources.ResourceGroupsClient\n\tstorageClient  *armstorage.AccountsClient\n}\n\n// NewStorageFactory instantiates an Azure Storage factory for building an Azure Storage playground\nfunc NewStorageFactory(subscriptionID string) *StorageFactory {\n\tcred := HandleErrWithResult(azidentity.NewDefaultAzureCredential(nil))\n\treturn &StorageFactory{\n\t\tcred:           cred,\n\t\tsubscriptionID: subscriptionID,\n\t\tgroupsClient:   BuildClient(subscriptionID, cred, armresources.NewResourceGroupsClient),\n\t\tstorageClient:  BuildClient(subscriptionID, cred, armstorage.NewAccountsClient),\n\t}\n}\n\nfunc (sf *StorageFactory) CreateStorageStack(ctx context.Context, location string) *StorageStack {\n\tstack := &StorageStack{\n\t\tname: haiku.Haikunate(),\n\t}\n\tstack.ResourceGroup = sf.createResourceGroup(ctx, stack.name, location)\n\tstack.Account = sf.createStorageAccount(ctx, stack.name, location)\n\tstack.AccountKey = sf.getPrimaryAccountKey(ctx, stack)\n\treturn stack\n}\n\nfunc (sf *StorageFactory) DestroyStorageStack(ctx context.Context, stack *StorageStack) {\n\t_, err := sf.groupsClient.BeginDelete(ctx, stack.name, nil)\n\tHandleErr(err)\n}\n\n// createResourceGroup creates an Azure resource by name and in a given location\nfunc (sf *StorageFactory) createResourceGroup(ctx context.Context, name, location string) armresources.ResourceGroup {\n\tparam := armresources.ResourceGroup{\n\t\tLocation: to.Ptr(location),\n\t}\n\n\tfmt.Printf(\"Building an Azure Resource Group named %q...\\n\", name)\n\tres, err := sf.groupsClient.CreateOrUpdate(ctx, name, param, nil)\n\tHandleErr(err)\n\treturn res.ResourceGroup\n}\n\n// createStorageAccount creates an Azure Storage Account\nfunc (sf *StorageFactory) createStorageAccount(ctx context.Context, name, location string) armstorage.Account {\n\tparam := armstorage.AccountCreateParameters{\n\t\tLocation: to.Ptr(location),\n\t\tKind:     to.Ptr(armstorage.KindBlockBlobStorage),\n\t\tSKU: &armstorage.SKU{\n\t\t\tName: to.Ptr(armstorage.SKUNamePremiumLRS),\n\t\t\tTier: to.Ptr(armstorage.SKUTierPremium),\n\t\t},\n\t}\n\n\taccountName := strings.Replace(name, \"-\", \"\", -1)\n\tfmt.Printf(\"Building an Azure Storage Account named %q...\\n\", accountName)\n\tpoller, err := sf.storageClient.BeginCreate(ctx, name, accountName, param, nil)\n\tHandleErr(err)\n\tres := HandleErrPoller(ctx, poller)\n\treturn res.Account\n}\n\nfunc (sf *StorageFactory) getPrimaryAccountKey(ctx context.Context, stack *StorageStack) *armstorage.AccountKey {\n\tfmt.Printf(\"Fetching the Azure Storage Account shared key...\\n\")\n\tres, err := sf.storageClient.ListKeys(ctx, stack.name, *stack.Account.Name, nil)\n\tHandleErr(err)\n\treturn res.Keys[0]\n}\n\nfunc (ss *StorageStack) ServiceClient() *azblob.ServiceClient {\n\tcred := HandleErrWithResult(azblob.NewSharedKeyCredential(*ss.Account.Name, *ss.AccountKey.Value))\n\tblobURI := *ss.Account.Properties.PrimaryEndpoints.Blob\n\tclient, err := azblob.NewServiceClientWithSharedKey(blobURI, cred, nil)\n\tHandleErr(err)\n\treturn client\n}\n"
  },
  {
    "path": "chapter/15/readme.md",
    "content": "# Programming the Cloud\n\nThis chapter illustrates the basics of manipulating and using cloud infrastructure. We build two stacks of infrastructure, a Virtual Machine and related infra, and a blob storage account and related infra. Through this code, we can learn about the Azure control plane (Azure Resource Manager) and the Azure Storage blob data plane.\n\n**NOTE:** Please DO NOT handle errors like shown in these examples. The error handling is abbreviated to make the code more concise for readability in the book. Panic is not your friend!\n\n## Tools needed\n- Azure CLI\n\n## Getting Started\nThe following steps will help you get started running the examples. Each of the steps should be run from the root directory of this chapter.\n\n### Generating SSH keys\nLets create an SSH key to use for logging into our Virtual Machine.\n```shell\n$ mkdir .ssh\n$ ssh-keygen -t rsa -b 4096\nGenerating public/private rsa key pair.\nEnter file in which to save the key (/Users/user/.ssh/id_rsa): ./.ssh/id_rsa\nEnter passphrase (empty for no passphrase):\nEnter same passphrase again:\nYour identification has been saved in ./.ssh/id_rsa\nYour public key has been saved in ./.ssh/id_rsa.pub\nThe key fingerprint is:\nSHA256:WtZQylkZaSjC24LWDw78wqqz8PJm+1eas54xy6Dk0ws user@computer\nThe key's randomart image is:\n+---[RSA 4096]----+\n|   .     .++     |\n|    o ...=+      |\n| . o + .=.       |\n|  = = .  o       |\n| o + +  S .      |\n|  o o .+.        |\n|..Eo. ++         |\n|=++o.o==         |\n|+B*+o+*o         |\n+----[SHA256]-----+\n```\n\n### Login to the Azure CLI\nNow that we have our SSH key setup, lets login to the Azure CLI.\n```shell\naz login\n```\n\n### Create .env file\nThe following command will create a .env file which is used to load environment vars in the examples.\n```shell\necho -e \"AZURE_SUBSCRIPTION_ID=$(az account show --query 'id' -o tsv)\\nSSH_PUBLIC_KEY_PATH=./.ssh/id_rsa.pub\" >> .env\n```\n\n## Run the examples\nEach of the examples below when executed will describe what operations are taking place and prompt you to interact with the infrastructure provisioned. Once done playing with the provisioned infra, just press enter in the terminal and the infrastructure will be destroyed.\n\n### Building an Azure Virtual Machine and related infrastructure\nThis example will build an Azure Resource Group, networking infra, and a Virtual Machine. After the VM is built, you can SSH into the machine and explore. The provisioned Virtual Machine runs the cloud-init provisioning script in `./cloud-init/init.yml` upon creation.\n```shell\ngo run ./cmd/compute/main.go\n```\n\n### Building an Azure Storage account, related infra, and storing files\nThis example will build an Azure Resource Group and a blob Storage Account which we will use to store some local images from the `./blobs` directory in the private cloud blob store. The example will then print signed URIs which will grant limited access to the images stored in the storage account.\n```shell\ngo run ./cmd/storage/main.go\n```\n\n"
  },
  {
    "path": "chapter/16/workflow/README.md",
    "content": "# A Generic Workflow Service\n\n![Diskerase runthrough](docs/images/client_runthrough.gif)\n(Our diskerase client running a workflow on the server)\n\n## Introduction\n\nThe example code layed out in this directory represents a generic workflow execution service. This service receives a protocol buffer to a gRPC service that represents the type of work we want to do (a WorkReq).\n\nThis is another example of separating the work to be done into two parts:\n\n* Creation of the work in one program\n* Execution of work in another\n\nThis allows centralization of all work done into a single system that can have security, emergency stop capabilities, policies, ... in a single location.  Clients that create work can have their own logic and tests. This benefits with:\n\n* Central place to create reusable components\n* Central policy enforcement\n* One system authorized for changes instead of multiple\n* Work logic is a separate system from work execution\n* One place to stop bad things when they are occuring\n\nThe work is defined in `Block`s with one block executed at at time. Inside the `Block`s are `Job`s, which are the actions that are taken. Those will be executed concurrently within some rate limit you define for the `Block`.\n\nEach `WorkReq` that is sent to the service is checked against a set of policies. If no policies are defined, the `WorkReq` is rejected. If the `WorkReq` violates a policy, it is rejected. Policies can be used to sanity check a `WorkReq`.\n\nOnce a `WorkReq` is received, a unique ID is generated and returned to the client. To execute that `WorkReq`, a second call to the server is made.\n\nThe server provides an RPC endoint to recover the status of the `WorkReq` for watching workflows execute.\n\n`Job`s and `Policies` can be added to the system to expand its capabilities.\n\nWe include some sample data that is used to represents \"sites\", or places where machines are located.  We also include data that represents \"machines\" at those sites. These obey some naming conventions and I have included the generators I used to make this fake data.  These stand in for what would probably be a database or services that would hold authoritative information.\n\nFinally I include a client that build `WorkReq` protocol buffers and call the service for a sample satellite disk erasure. You can use this to test that policies such as the token bucket work. You can alter these to try to defy the policies on the server.\n\n## What this isn't\n\nThis is an example of a generic workflow system to demonstrate concepts from our chapter on \"Designing for Chaos\". \n\nThis isn't a production quality service. If it has tests, they are not comprehensive. Unfortunately I have another full time job, so testing suffered for these book examples (something I would not do in my real job. Tests, tests and more tests!).\n\nOther things that make it non-production quality:\n\n* If we have a server restart, we cannot resume running workflows, we leave half eaten carcasses of workflows just laying around\n* There is no security, so anyone could call this service. By default it starts on 127.0.0.1:8080 and doesn't have Jobs that do anything bad, but if you decide to change that, you need security\n* Backend storage is local files in a temp directory\n* Failures do not have some maximum count, they only stop work if a Job decideds they are fatal\n* We don't write creations, start and end times\n* There is no web interface\n* Didn't provide a workflow killer except through emergency stop\n* No pause capabilities\n* No workflow cloning tools\n* ...\n\nThe one example workflow I put in here is a diskerase for satellite datacenters. The `diskErase` `Job` isn't real, it just sleeps. The other jobs are simply looking at files representing information about fake sites and machines. These jobs could be made real, but for this demo I didn't want to actually mutate anything real.\n\nYou could turn this into a real system, but it would need some more bells and whistles.  This is a very lightweight version of a system I developed at Google. That service could handle service failures, restarts, horizontally scale and lots had lots of helpful packages... \n\nThis is not that system.\n\n## Structure overview\n\n```\n├── client\n├── configs\n├── data\n│   ├── generators\n│   │   └── mk\n│   └── packages\n│       └── sites\n├── internal\n│   ├── es\n│   ├── policy\n│   │   ├── config\n│   │   └── register\n│   │       ├── restrictjobtypes\n│   │       ├── sameargs\n│   │       └── startorend\n│   ├── service\n│   │   ├── executor\n│   │   └── jobs\n│   │       └── register\n│   │           ├── diskerase\n│   │           ├── sleep\n│   │           ├── tokenbucket\n│   │           └── validatedecom\n│   └── token\n├── proto\n└── samples\n    └── diskerase\n        └── cmd\n```\n\n* `client/` contains a client library for talking to the service\n* `configs/` contains server configuration files, like our policies and emergency stop\n* `data/` contains fake data related to fake datacenters and machines\n\t* `generators/` has programs that generate our fake data\n\t* `packages/` has packages for reading our fake data\n* `internal/` contains the server's internal packages\n\t* `es/` provides a package for reading emergency stop data\n\t* `policy/` defines our policy engine and registered policies\n\t\t* `config/` has a policy configuration file reader\n\t\t* `register/` has a policy register and sub-directories containing policies in the system\n\t* `service/` contains the service implementation\n\t\t* `executor/` holds the main execution engine for all workflows\n\t\t\t* `jobs` contains our job execution engine and all defined jobs in the system\n\t\t\t\t* `register/` has a job regiter and sub-directories containing jobs defined for the system\n\t* `token/` has a token bucket implemention\n* `proto/` has the protocol buffer implementations used in the service, including how to define a workflow request\n* `samples/` contains sample workflow creation programs that can submit to the workflow service\n\t* `diskerase/` contains a client for creating satellite disk erase workflows for the service to execute\n\n## Finding Jobs that are available\n\nThe Jobs you can call are defined in: `internal/service/jobs/register/...`\n\nEach file header in the directory will give informations such as:\n```\nRegister name: \"diskErase\"\nArgs:\n\t\"machine\"(mandatory): The name of the machine, like \"aa01\" or \"ab02\"\n\t\"site\"(mandatory): The name of the site, like \"aaa\" or \"aba\"\nResult:\n\tErases a disk on a machine, except this is a demo, so it really just sleeps for 30 seconds.\n```\n\nThis let's you know what arguements to use with a `Job` you define. So if this was a `Job` I wanted to call, I might do:\n\n```go\njob := &pb.Job{\n\tName: \"diskErase\",\n\tArgs: map[string]string{\n\t\t\"machine\": \"aa01\",\n\t\t\"site\": \"aba02\",\n\t}\n}\n```\n\nYou can see the `samples/diskerase` sample program to see a client program in action.\n\n## Where to find policies\n\nAll policy implementations are define at: `internal/policy/register/...`\n\nIn each file you will see a call called: `policy.Register(\"startOrEnd\", p)` where \"startOrEnd\" is the name of the policy. The `struct` called `Settings` will give all the settings for a policy to be applied to a workflow.\n\nPolicies to apply to a workflow are defined in: `configs/policies.json`\n\nYou must have a policy entry for every type of `WorkReq` you want to submit inside `configs/policies.json`. This is checked against `WorkReq.Name`.\n\n## A satellite disk erasure client\n\nYou can find our example client that submits a datacenter satellite to have its disks erased at:\n`samples/diskerase`\n\nThe following are instructions on running our `diskerase` client against the workflow system  (remember that this code doesn't actually erase any disks or do any real work, we are just faking it).\n\n* Open a terminal\n* Enter the `workflow/` directory\n* Type: `go run workflow.go`\n\nYou should see some startup information like so:\n\n```\nRegistered Job:  diskErase\nRegistered Job:  sleep\nRegistered Job:  tokenBucket\nRegistered Job:  validateDecom\nRegistered Policy:  restrictJobTypes\nRegistered Policy:  sameArgs\nRegistered Policy:  startOrEnd\nWorkflow Storage is at:  /var/folders/rd/hbhb8s197633_f8ncy6fmpqr0000gn/T/workflows\nServer started\n```\n\nNow that we have the service running, let's start a satellite workflow:\n\n* Open another terminal\n* Enter the `workflow/samples/diskerase/` directory\n* Type: `go run diskerase.go eraseSatellite aap`\n\nThis asks our `diskerase` client to create a `pb.WorkReq` representing a disk erasure for cluster \"aap\". Our client will do some pre-checks and then create the `pb.WorkReq`, submit it to the system and then ask the system to execute it. A file: \"submit.log\" will be created that holds any UUIDs for workflow you create.\n\nIt will then display a message like so:\n\n![Diskerase status](docs/images/diskerase_status.png)\n\nOnce it has finished, you can access the full proto JSON output with:\n`go run diskerase.go statusProto [workflow id]`\n\nOr if you cancel out and want to resume watching, you can do:\n`go run diskerase.go status [workflow id]`\n\n## Some cool things to try\n\nNow that you have seen the client and server, you can watch some of the concepts from the chaos chapter in action by trying to do things that you shouldn't.\n\nHere are a few things to try out:\n\n### Run a diskerase and then try to run another diskerase\n\nThis is the simplest thing to try, as it requires no changes to files. This should trigger the token bucket in the pre-conidtions block and fail. Only 1 of these can be triggered per hour.\n\n### Run a diskerase on a non-satellite datacenter\n\nThis should fail because the tool only supports satellite datacenter types.\n\n### Run a diskerase on a non-decom'd satellite\n\nThis should fail various pre-checks because it is not in a decom state.\n\n### Make changes to es.json\n\nChange `configs/es.json` so that the `diskErase` entry has `stop` instead of `go` while running a workflow. `es.go` checks that file every 10 seconds and the display refreshes every 10 seconds. You can watch the workflow stop.\n\nYou can try other things here like erasing the entry, which will have the same effect (or not having it in the right JSON format).\n\nYou can erase the entry and try submitting a new workflow, which will get denied.\n\n### Make changes to the diskerase Jobs\n\nYou can change the Jobs that the `diskerase` client creates. You could add machines not in the same site, or remove precondition checks. These should violate policies and reject your jobs.\n\n### Change the diskerase pb.WorkReq.Name\n\nThis should cause the service to not have a policy and automatically reject the workflow.\n"
  },
  {
    "path": "chapter/16/workflow/client/client.go",
    "content": "/*\nPackage client provides access to the workflow service. You can use this client to:\n\tSubmit a *pb.WorkReq to the service\n\tExecute a *pb.WorkReq previously submitted\n\tGet the status of a *pb.WorkReq\n\nSee the README.md in the root workflow/ directory for more information.\n\nSECURITY NOTICE: As this is an example for a book and is meant to be run in a secure environment, we\nuse grpc.WithInsecure().  Aka, not production ready.\n*/\npackage client\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/cenkalti/backoff\"\n\t\"github.com/sony/gobreaker\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n\t\"google.golang.org/protobuf/proto\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/proto\"\n)\n\n// Workflow represents our Workflow client. It uses a builtin circuit breaker wrapping\n// an exponential backoff. The context passed to any method should be the maximum time you are\n// willing to wait including all retries. If a call returns an error because the context expires,\n// the circuit breaker will trip. Errors without gRPC status codes or with status codes of\n// DeadlineExceeded or ResourceExhausted are considered fatal errors. Fatal errors do not\n// get retries and do not trip the circuit breaker.\ntype Workflow struct {\n\tconn   *grpc.ClientConn\n\tclient pb.WorkflowClient\n\n\tcb        *gobreaker.CircuitBreaker\n\tretryPool sync.Pool\n}\n\n// New creates a new Workflow instance.\nfunc New(addr string) (*Workflow, error) {\n\tconn, err := grpc.Dial(addr, grpc.WithInsecure())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Workflow{\n\t\tconn:   conn,\n\t\tclient: pb.NewWorkflowClient(conn),\n\t\tcb: gobreaker.NewCircuitBreaker(\n\t\t\tgobreaker.Settings{\n\t\t\t\tMaxRequests: 1,\n\t\t\t\tInterval:    20 * time.Second,\n\t\t\t\tTimeout:     30 * time.Second,\n\t\t\t\tReadyToTrip: func(c gobreaker.Counts) bool {\n\t\t\t\t\treturn c.ConsecutiveFailures > 1\n\t\t\t\t},\n\t\t\t\tIsSuccessful: func(err error) bool {\n\t\t\t\t\tif isFatal(err) {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t\treturn false\n\t\t\t\t},\n\t\t\t},\n\t\t),\n\t\tretryPool: sync.Pool{\n\t\t\tNew: func() interface{} {\n\t\t\t\treturn backoff.NewExponentialBackOff()\n\t\t\t},\n\t\t},\n\t}, nil\n}\n\n// Submit submits a pb.WorkReq to the server. If successful an ID will be returned that\n// represents the pb.WorkReq on the server. This can be used in an Exec() call to execute\n// the pb.WorkReq.\nfunc (w *Workflow) Submit(ctx context.Context, req *pb.WorkReq) (string, error) {\n\tcaller := func(ctx context.Context, req proto.Message) (proto.Message, error) {\n\t\tr := req.(*pb.WorkReq)\n\t\treturn w.client.Submit(ctx, r)\n\t}\n\tresp, err := w.call(ctx, req, caller)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn resp.(*pb.WorkResp).Id, nil\n}\n\n// Exec causes the server to execute a pb.WorkReq that was previously accepted by the server\n// via a Submit() call.\nfunc (w *Workflow) Exec(ctx context.Context, id string) error {\n\tcaller := func(ctx context.Context, req proto.Message) (proto.Message, error) {\n\t\tr := req.(*pb.ExecReq)\n\t\treturn w.client.Exec(ctx, r)\n\t}\n\t_, err := w.call(ctx, &pb.ExecReq{Id: id}, caller)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// Status returns the status of a pb.WorkReq that was submitted to the server via the Submit()\n// call.\nfunc (w *Workflow) Status(ctx context.Context, id string) (*pb.StatusResp, error) {\n\tcaller := func(ctx context.Context, req proto.Message) (proto.Message, error) {\n\t\tr := req.(*pb.StatusReq)\n\t\treturn w.client.Status(ctx, r)\n\t}\n\tresp, err := w.call(ctx, &pb.StatusReq{Id: id}, caller)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn resp.(*pb.StatusResp), nil\n}\n\ntype grpcCall = func(context.Context, proto.Message) (proto.Message, error)\n\n// call generically calls any non-streaming gRPC endpoint that is contained within \"call\".\n// This method will default to a timeout of 30 seconds unless Context has a deadline.\n// We also have use a circuit breaker surrounding an exponential backoff on our calls. We\n// only do retries when we receive a generic error (one that does not contain a gRPC status code)\n// or status code: DeadlineExceeded, ResourceExhausted.\nfunc (w *Workflow) call(ctx context.Context, req proto.Message, call grpcCall) (proto.Message, error) {\n\tif _, ok := ctx.Deadline(); !ok {\n\t\tvar cancel context.CancelFunc\n\t\tctx, cancel = context.WithTimeout(ctx, 30*time.Second)\n\t\tdefer cancel()\n\t}\n\n\tvar resp proto.Message\n\top := func() error {\n\t\tvar err error\n\t\tresp, err = call(ctx, req)\n\t\treturn err\n\t}\n\n\tretry := func() error {\n\t\tback := w.retryPool.Get().(*backoff.ExponentialBackOff)\n\t\tdefer w.retryPool.Put(back)\n\t\tdefer back.Reset()\n\n\t\t// Execute our op() func until the context is cancelled with exponential backoff.\n\t\tfor {\n\t\t\terr := op()\n\t\t\tif err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tif isFatal(err) {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\t// We manually are going to do our backoff instead of using backoff.Retry()\n\t\t\t// because we want to differentiate between fatal errors that should never\n\t\t\t// get retried and errors that just need backing off.\n\t\t\tbackoff := back.NextBackOff()\n\t\t\tdeadline, _ := ctx.Deadline()\n\t\t\tif time.Now().Add(backoff).After(deadline) {\n\t\t\t\t// This happens when our next retry would happen after our deadline.\n\t\t\t\treturn context.DeadlineExceeded\n\t\t\t}\n\n\t\t\t// Sleep before our next retry on whatever backoff we were given or\n\t\t\t// until our context is cancelled by the user.\n\t\t\ttimer := time.NewTimer(backoff)\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\ttimer.Stop()\n\t\t\t\treturn err\n\t\t\tcase <-timer.C:\n\t\t\t}\n\t\t}\n\t}\n\n\t_, err := w.cb.Execute(\n\t\tfunc() (interface{}, error) {\n\t\t\treturn nil, retry()\n\t\t},\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn resp, nil\n}\n\nfunc isFatal(err error) bool {\n\tif s, ok := status.FromError(err); ok {\n\t\tswitch s.Code() {\n\t\tcase codes.DeadlineExceeded, codes.ResourceExhausted:\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "chapter/16/workflow/configs/README",
    "content": "This directory contains configuration files for running our service.\n"
  },
  {
    "path": "chapter/16/workflow/configs/es.json",
    "content": "{\n\t\"Name\": \"SatelliteDiskErase\",\n\t\"Status\": \"go\"\n}"
  },
  {
    "path": "chapter/16/workflow/configs/policies.json",
    "content": "{\n\t\"Name\": \"SatelliteDiskErase\",\n\t\"Policies\": [\n\t\t{\n\t\t\t\"Name\": \"restrictJobTypes\",\n\t\t\t\"Settings\": {\n\t\t\t\t\"AllowedJobs\": [\n\t\t\t\t        \"validateDecom\",\n        \t\t\t\t\"diskErase\",\n        \t\t\t\t\"sleep\",\n        \t\t\t\t\"tokenBucket\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Name\": \"sameArgs\",\n\t\t\t\"Settings\": {\n\t\t\t\t\"Jobs\": {\n\t\t\t\t\t\"diskErase\": [\n\t\t\t\t\t\t\"Site\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Name\": \"startOrEnd\",\n\t\t\t\"Settings\": {\n\t\t\t\t\"JobName\": \"tokenBucket\",\n\t\t\t\t\"MustArgs\": {\n\t\t\t\t\t\"bucket\": \"diskEraseSatellite\"\n\t\t\t\t},\n\t\t\t\t\"Start\": true,\n\t\t\t\t\"AllowedBeforeOrAfter\": [\n\t\t\t\t\t\"validateDecom\"\n\t\t\t\t]\n\t\t\t}\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "chapter/16/workflow/data/README.md",
    "content": "# Data directory\n\nThis simply contains some sample data that represents fictional datacenter sites and fictional machine information that is contained in those sites.\n\nTwo subdirectories are contained here:\n* `generators/` contains programs that are used to generate these data files\n* `packages/` contains packages that access these data files\n\nIn the real world the data would be in some service that you query for this information. But for our purposes, this gives us some fake data to be able to generate work to do."
  },
  {
    "path": "chapter/16/workflow/data/generators/README",
    "content": "This directory simply contains programs used to generate example data files."
  },
  {
    "path": "chapter/16/workflow/data/generators/mk/README",
    "content": "This program simply creates a file that contains data about our sites and machines that are in those sites.\nThere are sites that are not satellites in the sites file, but no correpsonding machines as those aren't needed.\n\nThe generated files are in the data/ directory for this exercise.\n"
  },
  {
    "path": "chapter/16/workflow/data/generators/mk/mk.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"os\"\n)\n\ntype site struct {\n\tName   string\n\tType   string\n\tStatus string\n}\n\ntype machine struct {\n\tName string\n\tSite string\n}\n\nfunc main() {\n\tfirst := byte(97)\n\tsecond := byte(97)\n\tthird := byte(97)\n\n\tsites := []site{}\n\tfor i := 0; i < 100; i++ {\n\t\tt := \"cluster\"\n\t\tif i%3 == 0 {\n\t\t\tt = \"satellite\"\n\t\t}\n\t\tsite := site{\n\t\t\tName:   string(append([]byte{}, first, second, third)),\n\t\t\tType:   t,\n\t\t\tStatus: \"inService\",\n\t\t}\n\t\tswitch site.Name {\n\t\tcase \"aap\", \"adg\", \"adv\":\n\t\t\tsite.Status = \"decom\"\n\t\t}\n\t\tsites = append(sites, site)\n\n\t\tif third < 122 {\n\t\t\tthird++\n\t\t} else {\n\t\t\tthird = 97\n\t\t\tsecond++\n\t\t\tif second == 122 {\n\t\t\t\tsecond = 97\n\t\t\t\tfirst++\n\t\t\t}\n\t\t}\n\t}\n\tmachines := []machine{}\n\tfor _, site := range sites {\n\t\tif site.Type == \"satellite\" {\n\t\t\tfirst := byte(97)\n\t\t\tsecond := byte(97)\n\t\t\ta := byte(48)\n\t\t\tb := byte(48)\n\t\t\tfor i := 0; i < 50; i++ {\n\t\t\t\tname := append([]byte{}, first, second, a, b)\n\t\t\t\tmachines = append(\n\t\t\t\t\tmachines,\n\t\t\t\t\tmachine{Name: string(name), Site: site.Name},\n\t\t\t\t)\n\t\t\t\tb++\n\t\t\t\tif b > 57 {\n\t\t\t\t\ta = 48\n\t\t\t\t\tb = 48\n\t\t\t\t\tsecond++\n\t\t\t\t\tif second > 122 {\n\t\t\t\t\t\tfirst++\n\t\t\t\t\t\tsecond = 97\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tsitef, err := os.OpenFile(\"sites.json\", os.O_WRONLY+os.O_CREATE, 0600)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer sitef.Close()\n\tmachinef, err := os.OpenFile(\"machines.json\", os.O_WRONLY+os.O_CREATE, 0600)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer machinef.Close()\n\n\tencSites := json.NewEncoder(sitef)\n\tfor _, s := range sites {\n\t\tif err := encSites.Encode(s); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\tencMachines := json.NewEncoder(machinef)\n\tfor _, m := range machines {\n\t\tif err := encMachines.Encode(m); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "chapter/16/workflow/data/machines.json",
    "content": "{\"Name\":\"aa00\",\"Site\":\"aaa\"}\n{\"Name\":\"aa01\",\"Site\":\"aaa\"}\n{\"Name\":\"aa02\",\"Site\":\"aaa\"}\n{\"Name\":\"aa03\",\"Site\":\"aaa\"}\n{\"Name\":\"aa04\",\"Site\":\"aaa\"}\n{\"Name\":\"aa05\",\"Site\":\"aaa\"}\n{\"Name\":\"aa06\",\"Site\":\"aaa\"}\n{\"Name\":\"aa07\",\"Site\":\"aaa\"}\n{\"Name\":\"aa08\",\"Site\":\"aaa\"}\n{\"Name\":\"aa09\",\"Site\":\"aaa\"}\n{\"Name\":\"ab00\",\"Site\":\"aaa\"}\n{\"Name\":\"ab01\",\"Site\":\"aaa\"}\n{\"Name\":\"ab02\",\"Site\":\"aaa\"}\n{\"Name\":\"ab03\",\"Site\":\"aaa\"}\n{\"Name\":\"ab04\",\"Site\":\"aaa\"}\n{\"Name\":\"ab05\",\"Site\":\"aaa\"}\n{\"Name\":\"ab06\",\"Site\":\"aaa\"}\n{\"Name\":\"ab07\",\"Site\":\"aaa\"}\n{\"Name\":\"ab08\",\"Site\":\"aaa\"}\n{\"Name\":\"ab09\",\"Site\":\"aaa\"}\n{\"Name\":\"ac00\",\"Site\":\"aaa\"}\n{\"Name\":\"ac01\",\"Site\":\"aaa\"}\n{\"Name\":\"ac02\",\"Site\":\"aaa\"}\n{\"Name\":\"ac03\",\"Site\":\"aaa\"}\n{\"Name\":\"ac04\",\"Site\":\"aaa\"}\n{\"Name\":\"ac05\",\"Site\":\"aaa\"}\n{\"Name\":\"ac06\",\"Site\":\"aaa\"}\n{\"Name\":\"ac07\",\"Site\":\"aaa\"}\n{\"Name\":\"ac08\",\"Site\":\"aaa\"}\n{\"Name\":\"ac09\",\"Site\":\"aaa\"}\n{\"Name\":\"ad00\",\"Site\":\"aaa\"}\n{\"Name\":\"ad01\",\"Site\":\"aaa\"}\n{\"Name\":\"ad02\",\"Site\":\"aaa\"}\n{\"Name\":\"ad03\",\"Site\":\"aaa\"}\n{\"Name\":\"ad04\",\"Site\":\"aaa\"}\n{\"Name\":\"ad05\",\"Site\":\"aaa\"}\n{\"Name\":\"ad06\",\"Site\":\"aaa\"}\n{\"Name\":\"ad07\",\"Site\":\"aaa\"}\n{\"Name\":\"ad08\",\"Site\":\"aaa\"}\n{\"Name\":\"ad09\",\"Site\":\"aaa\"}\n{\"Name\":\"ae00\",\"Site\":\"aaa\"}\n{\"Name\":\"ae01\",\"Site\":\"aaa\"}\n{\"Name\":\"ae02\",\"Site\":\"aaa\"}\n{\"Name\":\"ae03\",\"Site\":\"aaa\"}\n{\"Name\":\"ae04\",\"Site\":\"aaa\"}\n{\"Name\":\"ae05\",\"Site\":\"aaa\"}\n{\"Name\":\"ae06\",\"Site\":\"aaa\"}\n{\"Name\":\"ae07\",\"Site\":\"aaa\"}\n{\"Name\":\"ae08\",\"Site\":\"aaa\"}\n{\"Name\":\"ae09\",\"Site\":\"aaa\"}\n{\"Name\":\"aa00\",\"Site\":\"aad\"}\n{\"Name\":\"aa01\",\"Site\":\"aad\"}\n{\"Name\":\"aa02\",\"Site\":\"aad\"}\n{\"Name\":\"aa03\",\"Site\":\"aad\"}\n{\"Name\":\"aa04\",\"Site\":\"aad\"}\n{\"Name\":\"aa05\",\"Site\":\"aad\"}\n{\"Name\":\"aa06\",\"Site\":\"aad\"}\n{\"Name\":\"aa07\",\"Site\":\"aad\"}\n{\"Name\":\"aa08\",\"Site\":\"aad\"}\n{\"Name\":\"aa09\",\"Site\":\"aad\"}\n{\"Name\":\"ab00\",\"Site\":\"aad\"}\n{\"Name\":\"ab01\",\"Site\":\"aad\"}\n{\"Name\":\"ab02\",\"Site\":\"aad\"}\n{\"Name\":\"ab03\",\"Site\":\"aad\"}\n{\"Name\":\"ab04\",\"Site\":\"aad\"}\n{\"Name\":\"ab05\",\"Site\":\"aad\"}\n{\"Name\":\"ab06\",\"Site\":\"aad\"}\n{\"Name\":\"ab07\",\"Site\":\"aad\"}\n{\"Name\":\"ab08\",\"Site\":\"aad\"}\n{\"Name\":\"ab09\",\"Site\":\"aad\"}\n{\"Name\":\"ac00\",\"Site\":\"aad\"}\n{\"Name\":\"ac01\",\"Site\":\"aad\"}\n{\"Name\":\"ac02\",\"Site\":\"aad\"}\n{\"Name\":\"ac03\",\"Site\":\"aad\"}\n{\"Name\":\"ac04\",\"Site\":\"aad\"}\n{\"Name\":\"ac05\",\"Site\":\"aad\"}\n{\"Name\":\"ac06\",\"Site\":\"aad\"}\n{\"Name\":\"ac07\",\"Site\":\"aad\"}\n{\"Name\":\"ac08\",\"Site\":\"aad\"}\n{\"Name\":\"ac09\",\"Site\":\"aad\"}\n{\"Name\":\"ad00\",\"Site\":\"aad\"}\n{\"Name\":\"ad01\",\"Site\":\"aad\"}\n{\"Name\":\"ad02\",\"Site\":\"aad\"}\n{\"Name\":\"ad03\",\"Site\":\"aad\"}\n{\"Name\":\"ad04\",\"Site\":\"aad\"}\n{\"Name\":\"ad05\",\"Site\":\"aad\"}\n{\"Name\":\"ad06\",\"Site\":\"aad\"}\n{\"Name\":\"ad07\",\"Site\":\"aad\"}\n{\"Name\":\"ad08\",\"Site\":\"aad\"}\n{\"Name\":\"ad09\",\"Site\":\"aad\"}\n{\"Name\":\"ae00\",\"Site\":\"aad\"}\n{\"Name\":\"ae01\",\"Site\":\"aad\"}\n{\"Name\":\"ae02\",\"Site\":\"aad\"}\n{\"Name\":\"ae03\",\"Site\":\"aad\"}\n{\"Name\":\"ae04\",\"Site\":\"aad\"}\n{\"Name\":\"ae05\",\"Site\":\"aad\"}\n{\"Name\":\"ae06\",\"Site\":\"aad\"}\n{\"Name\":\"ae07\",\"Site\":\"aad\"}\n{\"Name\":\"ae08\",\"Site\":\"aad\"}\n{\"Name\":\"ae09\",\"Site\":\"aad\"}\n{\"Name\":\"aa00\",\"Site\":\"aag\"}\n{\"Name\":\"aa01\",\"Site\":\"aag\"}\n{\"Name\":\"aa02\",\"Site\":\"aag\"}\n{\"Name\":\"aa03\",\"Site\":\"aag\"}\n{\"Name\":\"aa04\",\"Site\":\"aag\"}\n{\"Name\":\"aa05\",\"Site\":\"aag\"}\n{\"Name\":\"aa06\",\"Site\":\"aag\"}\n{\"Name\":\"aa07\",\"Site\":\"aag\"}\n{\"Name\":\"aa08\",\"Site\":\"aag\"}\n{\"Name\":\"aa09\",\"Site\":\"aag\"}\n{\"Name\":\"ab00\",\"Site\":\"aag\"}\n{\"Name\":\"ab01\",\"Site\":\"aag\"}\n{\"Name\":\"ab02\",\"Site\":\"aag\"}\n{\"Name\":\"ab03\",\"Site\":\"aag\"}\n{\"Name\":\"ab04\",\"Site\":\"aag\"}\n{\"Name\":\"ab05\",\"Site\":\"aag\"}\n{\"Name\":\"ab06\",\"Site\":\"aag\"}\n{\"Name\":\"ab07\",\"Site\":\"aag\"}\n{\"Name\":\"ab08\",\"Site\":\"aag\"}\n{\"Name\":\"ab09\",\"Site\":\"aag\"}\n{\"Name\":\"ac00\",\"Site\":\"aag\"}\n{\"Name\":\"ac01\",\"Site\":\"aag\"}\n{\"Name\":\"ac02\",\"Site\":\"aag\"}\n{\"Name\":\"ac03\",\"Site\":\"aag\"}\n{\"Name\":\"ac04\",\"Site\":\"aag\"}\n{\"Name\":\"ac05\",\"Site\":\"aag\"}\n{\"Name\":\"ac06\",\"Site\":\"aag\"}\n{\"Name\":\"ac07\",\"Site\":\"aag\"}\n{\"Name\":\"ac08\",\"Site\":\"aag\"}\n{\"Name\":\"ac09\",\"Site\":\"aag\"}\n{\"Name\":\"ad00\",\"Site\":\"aag\"}\n{\"Name\":\"ad01\",\"Site\":\"aag\"}\n{\"Name\":\"ad02\",\"Site\":\"aag\"}\n{\"Name\":\"ad03\",\"Site\":\"aag\"}\n{\"Name\":\"ad04\",\"Site\":\"aag\"}\n{\"Name\":\"ad05\",\"Site\":\"aag\"}\n{\"Name\":\"ad06\",\"Site\":\"aag\"}\n{\"Name\":\"ad07\",\"Site\":\"aag\"}\n{\"Name\":\"ad08\",\"Site\":\"aag\"}\n{\"Name\":\"ad09\",\"Site\":\"aag\"}\n{\"Name\":\"ae00\",\"Site\":\"aag\"}\n{\"Name\":\"ae01\",\"Site\":\"aag\"}\n{\"Name\":\"ae02\",\"Site\":\"aag\"}\n{\"Name\":\"ae03\",\"Site\":\"aag\"}\n{\"Name\":\"ae04\",\"Site\":\"aag\"}\n{\"Name\":\"ae05\",\"Site\":\"aag\"}\n{\"Name\":\"ae06\",\"Site\":\"aag\"}\n{\"Name\":\"ae07\",\"Site\":\"aag\"}\n{\"Name\":\"ae08\",\"Site\":\"aag\"}\n{\"Name\":\"ae09\",\"Site\":\"aag\"}\n{\"Name\":\"aa00\",\"Site\":\"aaj\"}\n{\"Name\":\"aa01\",\"Site\":\"aaj\"}\n{\"Name\":\"aa02\",\"Site\":\"aaj\"}\n{\"Name\":\"aa03\",\"Site\":\"aaj\"}\n{\"Name\":\"aa04\",\"Site\":\"aaj\"}\n{\"Name\":\"aa05\",\"Site\":\"aaj\"}\n{\"Name\":\"aa06\",\"Site\":\"aaj\"}\n{\"Name\":\"aa07\",\"Site\":\"aaj\"}\n{\"Name\":\"aa08\",\"Site\":\"aaj\"}\n{\"Name\":\"aa09\",\"Site\":\"aaj\"}\n{\"Name\":\"ab00\",\"Site\":\"aaj\"}\n{\"Name\":\"ab01\",\"Site\":\"aaj\"}\n{\"Name\":\"ab02\",\"Site\":\"aaj\"}\n{\"Name\":\"ab03\",\"Site\":\"aaj\"}\n{\"Name\":\"ab04\",\"Site\":\"aaj\"}\n{\"Name\":\"ab05\",\"Site\":\"aaj\"}\n{\"Name\":\"ab06\",\"Site\":\"aaj\"}\n{\"Name\":\"ab07\",\"Site\":\"aaj\"}\n{\"Name\":\"ab08\",\"Site\":\"aaj\"}\n{\"Name\":\"ab09\",\"Site\":\"aaj\"}\n{\"Name\":\"ac00\",\"Site\":\"aaj\"}\n{\"Name\":\"ac01\",\"Site\":\"aaj\"}\n{\"Name\":\"ac02\",\"Site\":\"aaj\"}\n{\"Name\":\"ac03\",\"Site\":\"aaj\"}\n{\"Name\":\"ac04\",\"Site\":\"aaj\"}\n{\"Name\":\"ac05\",\"Site\":\"aaj\"}\n{\"Name\":\"ac06\",\"Site\":\"aaj\"}\n{\"Name\":\"ac07\",\"Site\":\"aaj\"}\n{\"Name\":\"ac08\",\"Site\":\"aaj\"}\n{\"Name\":\"ac09\",\"Site\":\"aaj\"}\n{\"Name\":\"ad00\",\"Site\":\"aaj\"}\n{\"Name\":\"ad01\",\"Site\":\"aaj\"}\n{\"Name\":\"ad02\",\"Site\":\"aaj\"}\n{\"Name\":\"ad03\",\"Site\":\"aaj\"}\n{\"Name\":\"ad04\",\"Site\":\"aaj\"}\n{\"Name\":\"ad05\",\"Site\":\"aaj\"}\n{\"Name\":\"ad06\",\"Site\":\"aaj\"}\n{\"Name\":\"ad07\",\"Site\":\"aaj\"}\n{\"Name\":\"ad08\",\"Site\":\"aaj\"}\n{\"Name\":\"ad09\",\"Site\":\"aaj\"}\n{\"Name\":\"ae00\",\"Site\":\"aaj\"}\n{\"Name\":\"ae01\",\"Site\":\"aaj\"}\n{\"Name\":\"ae02\",\"Site\":\"aaj\"}\n{\"Name\":\"ae03\",\"Site\":\"aaj\"}\n{\"Name\":\"ae04\",\"Site\":\"aaj\"}\n{\"Name\":\"ae05\",\"Site\":\"aaj\"}\n{\"Name\":\"ae06\",\"Site\":\"aaj\"}\n{\"Name\":\"ae07\",\"Site\":\"aaj\"}\n{\"Name\":\"ae08\",\"Site\":\"aaj\"}\n{\"Name\":\"ae09\",\"Site\":\"aaj\"}\n{\"Name\":\"aa00\",\"Site\":\"aam\"}\n{\"Name\":\"aa01\",\"Site\":\"aam\"}\n{\"Name\":\"aa02\",\"Site\":\"aam\"}\n{\"Name\":\"aa03\",\"Site\":\"aam\"}\n{\"Name\":\"aa04\",\"Site\":\"aam\"}\n{\"Name\":\"aa05\",\"Site\":\"aam\"}\n{\"Name\":\"aa06\",\"Site\":\"aam\"}\n{\"Name\":\"aa07\",\"Site\":\"aam\"}\n{\"Name\":\"aa08\",\"Site\":\"aam\"}\n{\"Name\":\"aa09\",\"Site\":\"aam\"}\n{\"Name\":\"ab00\",\"Site\":\"aam\"}\n{\"Name\":\"ab01\",\"Site\":\"aam\"}\n{\"Name\":\"ab02\",\"Site\":\"aam\"}\n{\"Name\":\"ab03\",\"Site\":\"aam\"}\n{\"Name\":\"ab04\",\"Site\":\"aam\"}\n{\"Name\":\"ab05\",\"Site\":\"aam\"}\n{\"Name\":\"ab06\",\"Site\":\"aam\"}\n{\"Name\":\"ab07\",\"Site\":\"aam\"}\n{\"Name\":\"ab08\",\"Site\":\"aam\"}\n{\"Name\":\"ab09\",\"Site\":\"aam\"}\n{\"Name\":\"ac00\",\"Site\":\"aam\"}\n{\"Name\":\"ac01\",\"Site\":\"aam\"}\n{\"Name\":\"ac02\",\"Site\":\"aam\"}\n{\"Name\":\"ac03\",\"Site\":\"aam\"}\n{\"Name\":\"ac04\",\"Site\":\"aam\"}\n{\"Name\":\"ac05\",\"Site\":\"aam\"}\n{\"Name\":\"ac06\",\"Site\":\"aam\"}\n{\"Name\":\"ac07\",\"Site\":\"aam\"}\n{\"Name\":\"ac08\",\"Site\":\"aam\"}\n{\"Name\":\"ac09\",\"Site\":\"aam\"}\n{\"Name\":\"ad00\",\"Site\":\"aam\"}\n{\"Name\":\"ad01\",\"Site\":\"aam\"}\n{\"Name\":\"ad02\",\"Site\":\"aam\"}\n{\"Name\":\"ad03\",\"Site\":\"aam\"}\n{\"Name\":\"ad04\",\"Site\":\"aam\"}\n{\"Name\":\"ad05\",\"Site\":\"aam\"}\n{\"Name\":\"ad06\",\"Site\":\"aam\"}\n{\"Name\":\"ad07\",\"Site\":\"aam\"}\n{\"Name\":\"ad08\",\"Site\":\"aam\"}\n{\"Name\":\"ad09\",\"Site\":\"aam\"}\n{\"Name\":\"ae00\",\"Site\":\"aam\"}\n{\"Name\":\"ae01\",\"Site\":\"aam\"}\n{\"Name\":\"ae02\",\"Site\":\"aam\"}\n{\"Name\":\"ae03\",\"Site\":\"aam\"}\n{\"Name\":\"ae04\",\"Site\":\"aam\"}\n{\"Name\":\"ae05\",\"Site\":\"aam\"}\n{\"Name\":\"ae06\",\"Site\":\"aam\"}\n{\"Name\":\"ae07\",\"Site\":\"aam\"}\n{\"Name\":\"ae08\",\"Site\":\"aam\"}\n{\"Name\":\"ae09\",\"Site\":\"aam\"}\n{\"Name\":\"aa00\",\"Site\":\"aap\"}\n{\"Name\":\"aa01\",\"Site\":\"aap\"}\n{\"Name\":\"aa02\",\"Site\":\"aap\"}\n{\"Name\":\"aa03\",\"Site\":\"aap\"}\n{\"Name\":\"aa04\",\"Site\":\"aap\"}\n{\"Name\":\"aa05\",\"Site\":\"aap\"}\n{\"Name\":\"aa06\",\"Site\":\"aap\"}\n{\"Name\":\"aa07\",\"Site\":\"aap\"}\n{\"Name\":\"aa08\",\"Site\":\"aap\"}\n{\"Name\":\"aa09\",\"Site\":\"aap\"}\n{\"Name\":\"ab00\",\"Site\":\"aap\"}\n{\"Name\":\"ab01\",\"Site\":\"aap\"}\n{\"Name\":\"ab02\",\"Site\":\"aap\"}\n{\"Name\":\"ab03\",\"Site\":\"aap\"}\n{\"Name\":\"ab04\",\"Site\":\"aap\"}\n{\"Name\":\"ab05\",\"Site\":\"aap\"}\n{\"Name\":\"ab06\",\"Site\":\"aap\"}\n{\"Name\":\"ab07\",\"Site\":\"aap\"}\n{\"Name\":\"ab08\",\"Site\":\"aap\"}\n{\"Name\":\"ab09\",\"Site\":\"aap\"}\n{\"Name\":\"ac00\",\"Site\":\"aap\"}\n{\"Name\":\"ac01\",\"Site\":\"aap\"}\n{\"Name\":\"ac02\",\"Site\":\"aap\"}\n{\"Name\":\"ac03\",\"Site\":\"aap\"}\n{\"Name\":\"ac04\",\"Site\":\"aap\"}\n{\"Name\":\"ac05\",\"Site\":\"aap\"}\n{\"Name\":\"ac06\",\"Site\":\"aap\"}\n{\"Name\":\"ac07\",\"Site\":\"aap\"}\n{\"Name\":\"ac08\",\"Site\":\"aap\"}\n{\"Name\":\"ac09\",\"Site\":\"aap\"}\n{\"Name\":\"ad00\",\"Site\":\"aap\"}\n{\"Name\":\"ad01\",\"Site\":\"aap\"}\n{\"Name\":\"ad02\",\"Site\":\"aap\"}\n{\"Name\":\"ad03\",\"Site\":\"aap\"}\n{\"Name\":\"ad04\",\"Site\":\"aap\"}\n{\"Name\":\"ad05\",\"Site\":\"aap\"}\n{\"Name\":\"ad06\",\"Site\":\"aap\"}\n{\"Name\":\"ad07\",\"Site\":\"aap\"}\n{\"Name\":\"ad08\",\"Site\":\"aap\"}\n{\"Name\":\"ad09\",\"Site\":\"aap\"}\n{\"Name\":\"ae00\",\"Site\":\"aap\"}\n{\"Name\":\"ae01\",\"Site\":\"aap\"}\n{\"Name\":\"ae02\",\"Site\":\"aap\"}\n{\"Name\":\"ae03\",\"Site\":\"aap\"}\n{\"Name\":\"ae04\",\"Site\":\"aap\"}\n{\"Name\":\"ae05\",\"Site\":\"aap\"}\n{\"Name\":\"ae06\",\"Site\":\"aap\"}\n{\"Name\":\"ae07\",\"Site\":\"aap\"}\n{\"Name\":\"ae08\",\"Site\":\"aap\"}\n{\"Name\":\"ae09\",\"Site\":\"aap\"}\n{\"Name\":\"aa00\",\"Site\":\"aas\"}\n{\"Name\":\"aa01\",\"Site\":\"aas\"}\n{\"Name\":\"aa02\",\"Site\":\"aas\"}\n{\"Name\":\"aa03\",\"Site\":\"aas\"}\n{\"Name\":\"aa04\",\"Site\":\"aas\"}\n{\"Name\":\"aa05\",\"Site\":\"aas\"}\n{\"Name\":\"aa06\",\"Site\":\"aas\"}\n{\"Name\":\"aa07\",\"Site\":\"aas\"}\n{\"Name\":\"aa08\",\"Site\":\"aas\"}\n{\"Name\":\"aa09\",\"Site\":\"aas\"}\n{\"Name\":\"ab00\",\"Site\":\"aas\"}\n{\"Name\":\"ab01\",\"Site\":\"aas\"}\n{\"Name\":\"ab02\",\"Site\":\"aas\"}\n{\"Name\":\"ab03\",\"Site\":\"aas\"}\n{\"Name\":\"ab04\",\"Site\":\"aas\"}\n{\"Name\":\"ab05\",\"Site\":\"aas\"}\n{\"Name\":\"ab06\",\"Site\":\"aas\"}\n{\"Name\":\"ab07\",\"Site\":\"aas\"}\n{\"Name\":\"ab08\",\"Site\":\"aas\"}\n{\"Name\":\"ab09\",\"Site\":\"aas\"}\n{\"Name\":\"ac00\",\"Site\":\"aas\"}\n{\"Name\":\"ac01\",\"Site\":\"aas\"}\n{\"Name\":\"ac02\",\"Site\":\"aas\"}\n{\"Name\":\"ac03\",\"Site\":\"aas\"}\n{\"Name\":\"ac04\",\"Site\":\"aas\"}\n{\"Name\":\"ac05\",\"Site\":\"aas\"}\n{\"Name\":\"ac06\",\"Site\":\"aas\"}\n{\"Name\":\"ac07\",\"Site\":\"aas\"}\n{\"Name\":\"ac08\",\"Site\":\"aas\"}\n{\"Name\":\"ac09\",\"Site\":\"aas\"}\n{\"Name\":\"ad00\",\"Site\":\"aas\"}\n{\"Name\":\"ad01\",\"Site\":\"aas\"}\n{\"Name\":\"ad02\",\"Site\":\"aas\"}\n{\"Name\":\"ad03\",\"Site\":\"aas\"}\n{\"Name\":\"ad04\",\"Site\":\"aas\"}\n{\"Name\":\"ad05\",\"Site\":\"aas\"}\n{\"Name\":\"ad06\",\"Site\":\"aas\"}\n{\"Name\":\"ad07\",\"Site\":\"aas\"}\n{\"Name\":\"ad08\",\"Site\":\"aas\"}\n{\"Name\":\"ad09\",\"Site\":\"aas\"}\n{\"Name\":\"ae00\",\"Site\":\"aas\"}\n{\"Name\":\"ae01\",\"Site\":\"aas\"}\n{\"Name\":\"ae02\",\"Site\":\"aas\"}\n{\"Name\":\"ae03\",\"Site\":\"aas\"}\n{\"Name\":\"ae04\",\"Site\":\"aas\"}\n{\"Name\":\"ae05\",\"Site\":\"aas\"}\n{\"Name\":\"ae06\",\"Site\":\"aas\"}\n{\"Name\":\"ae07\",\"Site\":\"aas\"}\n{\"Name\":\"ae08\",\"Site\":\"aas\"}\n{\"Name\":\"ae09\",\"Site\":\"aas\"}\n{\"Name\":\"aa00\",\"Site\":\"aav\"}\n{\"Name\":\"aa01\",\"Site\":\"aav\"}\n{\"Name\":\"aa02\",\"Site\":\"aav\"}\n{\"Name\":\"aa03\",\"Site\":\"aav\"}\n{\"Name\":\"aa04\",\"Site\":\"aav\"}\n{\"Name\":\"aa05\",\"Site\":\"aav\"}\n{\"Name\":\"aa06\",\"Site\":\"aav\"}\n{\"Name\":\"aa07\",\"Site\":\"aav\"}\n{\"Name\":\"aa08\",\"Site\":\"aav\"}\n{\"Name\":\"aa09\",\"Site\":\"aav\"}\n{\"Name\":\"ab00\",\"Site\":\"aav\"}\n{\"Name\":\"ab01\",\"Site\":\"aav\"}\n{\"Name\":\"ab02\",\"Site\":\"aav\"}\n{\"Name\":\"ab03\",\"Site\":\"aav\"}\n{\"Name\":\"ab04\",\"Site\":\"aav\"}\n{\"Name\":\"ab05\",\"Site\":\"aav\"}\n{\"Name\":\"ab06\",\"Site\":\"aav\"}\n{\"Name\":\"ab07\",\"Site\":\"aav\"}\n{\"Name\":\"ab08\",\"Site\":\"aav\"}\n{\"Name\":\"ab09\",\"Site\":\"aav\"}\n{\"Name\":\"ac00\",\"Site\":\"aav\"}\n{\"Name\":\"ac01\",\"Site\":\"aav\"}\n{\"Name\":\"ac02\",\"Site\":\"aav\"}\n{\"Name\":\"ac03\",\"Site\":\"aav\"}\n{\"Name\":\"ac04\",\"Site\":\"aav\"}\n{\"Name\":\"ac05\",\"Site\":\"aav\"}\n{\"Name\":\"ac06\",\"Site\":\"aav\"}\n{\"Name\":\"ac07\",\"Site\":\"aav\"}\n{\"Name\":\"ac08\",\"Site\":\"aav\"}\n{\"Name\":\"ac09\",\"Site\":\"aav\"}\n{\"Name\":\"ad00\",\"Site\":\"aav\"}\n{\"Name\":\"ad01\",\"Site\":\"aav\"}\n{\"Name\":\"ad02\",\"Site\":\"aav\"}\n{\"Name\":\"ad03\",\"Site\":\"aav\"}\n{\"Name\":\"ad04\",\"Site\":\"aav\"}\n{\"Name\":\"ad05\",\"Site\":\"aav\"}\n{\"Name\":\"ad06\",\"Site\":\"aav\"}\n{\"Name\":\"ad07\",\"Site\":\"aav\"}\n{\"Name\":\"ad08\",\"Site\":\"aav\"}\n{\"Name\":\"ad09\",\"Site\":\"aav\"}\n{\"Name\":\"ae00\",\"Site\":\"aav\"}\n{\"Name\":\"ae01\",\"Site\":\"aav\"}\n{\"Name\":\"ae02\",\"Site\":\"aav\"}\n{\"Name\":\"ae03\",\"Site\":\"aav\"}\n{\"Name\":\"ae04\",\"Site\":\"aav\"}\n{\"Name\":\"ae05\",\"Site\":\"aav\"}\n{\"Name\":\"ae06\",\"Site\":\"aav\"}\n{\"Name\":\"ae07\",\"Site\":\"aav\"}\n{\"Name\":\"ae08\",\"Site\":\"aav\"}\n{\"Name\":\"ae09\",\"Site\":\"aav\"}\n{\"Name\":\"aa00\",\"Site\":\"aay\"}\n{\"Name\":\"aa01\",\"Site\":\"aay\"}\n{\"Name\":\"aa02\",\"Site\":\"aay\"}\n{\"Name\":\"aa03\",\"Site\":\"aay\"}\n{\"Name\":\"aa04\",\"Site\":\"aay\"}\n{\"Name\":\"aa05\",\"Site\":\"aay\"}\n{\"Name\":\"aa06\",\"Site\":\"aay\"}\n{\"Name\":\"aa07\",\"Site\":\"aay\"}\n{\"Name\":\"aa08\",\"Site\":\"aay\"}\n{\"Name\":\"aa09\",\"Site\":\"aay\"}\n{\"Name\":\"ab00\",\"Site\":\"aay\"}\n{\"Name\":\"ab01\",\"Site\":\"aay\"}\n{\"Name\":\"ab02\",\"Site\":\"aay\"}\n{\"Name\":\"ab03\",\"Site\":\"aay\"}\n{\"Name\":\"ab04\",\"Site\":\"aay\"}\n{\"Name\":\"ab05\",\"Site\":\"aay\"}\n{\"Name\":\"ab06\",\"Site\":\"aay\"}\n{\"Name\":\"ab07\",\"Site\":\"aay\"}\n{\"Name\":\"ab08\",\"Site\":\"aay\"}\n{\"Name\":\"ab09\",\"Site\":\"aay\"}\n{\"Name\":\"ac00\",\"Site\":\"aay\"}\n{\"Name\":\"ac01\",\"Site\":\"aay\"}\n{\"Name\":\"ac02\",\"Site\":\"aay\"}\n{\"Name\":\"ac03\",\"Site\":\"aay\"}\n{\"Name\":\"ac04\",\"Site\":\"aay\"}\n{\"Name\":\"ac05\",\"Site\":\"aay\"}\n{\"Name\":\"ac06\",\"Site\":\"aay\"}\n{\"Name\":\"ac07\",\"Site\":\"aay\"}\n{\"Name\":\"ac08\",\"Site\":\"aay\"}\n{\"Name\":\"ac09\",\"Site\":\"aay\"}\n{\"Name\":\"ad00\",\"Site\":\"aay\"}\n{\"Name\":\"ad01\",\"Site\":\"aay\"}\n{\"Name\":\"ad02\",\"Site\":\"aay\"}\n{\"Name\":\"ad03\",\"Site\":\"aay\"}\n{\"Name\":\"ad04\",\"Site\":\"aay\"}\n{\"Name\":\"ad05\",\"Site\":\"aay\"}\n{\"Name\":\"ad06\",\"Site\":\"aay\"}\n{\"Name\":\"ad07\",\"Site\":\"aay\"}\n{\"Name\":\"ad08\",\"Site\":\"aay\"}\n{\"Name\":\"ad09\",\"Site\":\"aay\"}\n{\"Name\":\"ae00\",\"Site\":\"aay\"}\n{\"Name\":\"ae01\",\"Site\":\"aay\"}\n{\"Name\":\"ae02\",\"Site\":\"aay\"}\n{\"Name\":\"ae03\",\"Site\":\"aay\"}\n{\"Name\":\"ae04\",\"Site\":\"aay\"}\n{\"Name\":\"ae05\",\"Site\":\"aay\"}\n{\"Name\":\"ae06\",\"Site\":\"aay\"}\n{\"Name\":\"ae07\",\"Site\":\"aay\"}\n{\"Name\":\"ae08\",\"Site\":\"aay\"}\n{\"Name\":\"ae09\",\"Site\":\"aay\"}\n{\"Name\":\"aa00\",\"Site\":\"abb\"}\n{\"Name\":\"aa01\",\"Site\":\"abb\"}\n{\"Name\":\"aa02\",\"Site\":\"abb\"}\n{\"Name\":\"aa03\",\"Site\":\"abb\"}\n{\"Name\":\"aa04\",\"Site\":\"abb\"}\n{\"Name\":\"aa05\",\"Site\":\"abb\"}\n{\"Name\":\"aa06\",\"Site\":\"abb\"}\n{\"Name\":\"aa07\",\"Site\":\"abb\"}\n{\"Name\":\"aa08\",\"Site\":\"abb\"}\n{\"Name\":\"aa09\",\"Site\":\"abb\"}\n{\"Name\":\"ab00\",\"Site\":\"abb\"}\n{\"Name\":\"ab01\",\"Site\":\"abb\"}\n{\"Name\":\"ab02\",\"Site\":\"abb\"}\n{\"Name\":\"ab03\",\"Site\":\"abb\"}\n{\"Name\":\"ab04\",\"Site\":\"abb\"}\n{\"Name\":\"ab05\",\"Site\":\"abb\"}\n{\"Name\":\"ab06\",\"Site\":\"abb\"}\n{\"Name\":\"ab07\",\"Site\":\"abb\"}\n{\"Name\":\"ab08\",\"Site\":\"abb\"}\n{\"Name\":\"ab09\",\"Site\":\"abb\"}\n{\"Name\":\"ac00\",\"Site\":\"abb\"}\n{\"Name\":\"ac01\",\"Site\":\"abb\"}\n{\"Name\":\"ac02\",\"Site\":\"abb\"}\n{\"Name\":\"ac03\",\"Site\":\"abb\"}\n{\"Name\":\"ac04\",\"Site\":\"abb\"}\n{\"Name\":\"ac05\",\"Site\":\"abb\"}\n{\"Name\":\"ac06\",\"Site\":\"abb\"}\n{\"Name\":\"ac07\",\"Site\":\"abb\"}\n{\"Name\":\"ac08\",\"Site\":\"abb\"}\n{\"Name\":\"ac09\",\"Site\":\"abb\"}\n{\"Name\":\"ad00\",\"Site\":\"abb\"}\n{\"Name\":\"ad01\",\"Site\":\"abb\"}\n{\"Name\":\"ad02\",\"Site\":\"abb\"}\n{\"Name\":\"ad03\",\"Site\":\"abb\"}\n{\"Name\":\"ad04\",\"Site\":\"abb\"}\n{\"Name\":\"ad05\",\"Site\":\"abb\"}\n{\"Name\":\"ad06\",\"Site\":\"abb\"}\n{\"Name\":\"ad07\",\"Site\":\"abb\"}\n{\"Name\":\"ad08\",\"Site\":\"abb\"}\n{\"Name\":\"ad09\",\"Site\":\"abb\"}\n{\"Name\":\"ae00\",\"Site\":\"abb\"}\n{\"Name\":\"ae01\",\"Site\":\"abb\"}\n{\"Name\":\"ae02\",\"Site\":\"abb\"}\n{\"Name\":\"ae03\",\"Site\":\"abb\"}\n{\"Name\":\"ae04\",\"Site\":\"abb\"}\n{\"Name\":\"ae05\",\"Site\":\"abb\"}\n{\"Name\":\"ae06\",\"Site\":\"abb\"}\n{\"Name\":\"ae07\",\"Site\":\"abb\"}\n{\"Name\":\"ae08\",\"Site\":\"abb\"}\n{\"Name\":\"ae09\",\"Site\":\"abb\"}\n{\"Name\":\"aa00\",\"Site\":\"abe\"}\n{\"Name\":\"aa01\",\"Site\":\"abe\"}\n{\"Name\":\"aa02\",\"Site\":\"abe\"}\n{\"Name\":\"aa03\",\"Site\":\"abe\"}\n{\"Name\":\"aa04\",\"Site\":\"abe\"}\n{\"Name\":\"aa05\",\"Site\":\"abe\"}\n{\"Name\":\"aa06\",\"Site\":\"abe\"}\n{\"Name\":\"aa07\",\"Site\":\"abe\"}\n{\"Name\":\"aa08\",\"Site\":\"abe\"}\n{\"Name\":\"aa09\",\"Site\":\"abe\"}\n{\"Name\":\"ab00\",\"Site\":\"abe\"}\n{\"Name\":\"ab01\",\"Site\":\"abe\"}\n{\"Name\":\"ab02\",\"Site\":\"abe\"}\n{\"Name\":\"ab03\",\"Site\":\"abe\"}\n{\"Name\":\"ab04\",\"Site\":\"abe\"}\n{\"Name\":\"ab05\",\"Site\":\"abe\"}\n{\"Name\":\"ab06\",\"Site\":\"abe\"}\n{\"Name\":\"ab07\",\"Site\":\"abe\"}\n{\"Name\":\"ab08\",\"Site\":\"abe\"}\n{\"Name\":\"ab09\",\"Site\":\"abe\"}\n{\"Name\":\"ac00\",\"Site\":\"abe\"}\n{\"Name\":\"ac01\",\"Site\":\"abe\"}\n{\"Name\":\"ac02\",\"Site\":\"abe\"}\n{\"Name\":\"ac03\",\"Site\":\"abe\"}\n{\"Name\":\"ac04\",\"Site\":\"abe\"}\n{\"Name\":\"ac05\",\"Site\":\"abe\"}\n{\"Name\":\"ac06\",\"Site\":\"abe\"}\n{\"Name\":\"ac07\",\"Site\":\"abe\"}\n{\"Name\":\"ac08\",\"Site\":\"abe\"}\n{\"Name\":\"ac09\",\"Site\":\"abe\"}\n{\"Name\":\"ad00\",\"Site\":\"abe\"}\n{\"Name\":\"ad01\",\"Site\":\"abe\"}\n{\"Name\":\"ad02\",\"Site\":\"abe\"}\n{\"Name\":\"ad03\",\"Site\":\"abe\"}\n{\"Name\":\"ad04\",\"Site\":\"abe\"}\n{\"Name\":\"ad05\",\"Site\":\"abe\"}\n{\"Name\":\"ad06\",\"Site\":\"abe\"}\n{\"Name\":\"ad07\",\"Site\":\"abe\"}\n{\"Name\":\"ad08\",\"Site\":\"abe\"}\n{\"Name\":\"ad09\",\"Site\":\"abe\"}\n{\"Name\":\"ae00\",\"Site\":\"abe\"}\n{\"Name\":\"ae01\",\"Site\":\"abe\"}\n{\"Name\":\"ae02\",\"Site\":\"abe\"}\n{\"Name\":\"ae03\",\"Site\":\"abe\"}\n{\"Name\":\"ae04\",\"Site\":\"abe\"}\n{\"Name\":\"ae05\",\"Site\":\"abe\"}\n{\"Name\":\"ae06\",\"Site\":\"abe\"}\n{\"Name\":\"ae07\",\"Site\":\"abe\"}\n{\"Name\":\"ae08\",\"Site\":\"abe\"}\n{\"Name\":\"ae09\",\"Site\":\"abe\"}\n{\"Name\":\"aa00\",\"Site\":\"abh\"}\n{\"Name\":\"aa01\",\"Site\":\"abh\"}\n{\"Name\":\"aa02\",\"Site\":\"abh\"}\n{\"Name\":\"aa03\",\"Site\":\"abh\"}\n{\"Name\":\"aa04\",\"Site\":\"abh\"}\n{\"Name\":\"aa05\",\"Site\":\"abh\"}\n{\"Name\":\"aa06\",\"Site\":\"abh\"}\n{\"Name\":\"aa07\",\"Site\":\"abh\"}\n{\"Name\":\"aa08\",\"Site\":\"abh\"}\n{\"Name\":\"aa09\",\"Site\":\"abh\"}\n{\"Name\":\"ab00\",\"Site\":\"abh\"}\n{\"Name\":\"ab01\",\"Site\":\"abh\"}\n{\"Name\":\"ab02\",\"Site\":\"abh\"}\n{\"Name\":\"ab03\",\"Site\":\"abh\"}\n{\"Name\":\"ab04\",\"Site\":\"abh\"}\n{\"Name\":\"ab05\",\"Site\":\"abh\"}\n{\"Name\":\"ab06\",\"Site\":\"abh\"}\n{\"Name\":\"ab07\",\"Site\":\"abh\"}\n{\"Name\":\"ab08\",\"Site\":\"abh\"}\n{\"Name\":\"ab09\",\"Site\":\"abh\"}\n{\"Name\":\"ac00\",\"Site\":\"abh\"}\n{\"Name\":\"ac01\",\"Site\":\"abh\"}\n{\"Name\":\"ac02\",\"Site\":\"abh\"}\n{\"Name\":\"ac03\",\"Site\":\"abh\"}\n{\"Name\":\"ac04\",\"Site\":\"abh\"}\n{\"Name\":\"ac05\",\"Site\":\"abh\"}\n{\"Name\":\"ac06\",\"Site\":\"abh\"}\n{\"Name\":\"ac07\",\"Site\":\"abh\"}\n{\"Name\":\"ac08\",\"Site\":\"abh\"}\n{\"Name\":\"ac09\",\"Site\":\"abh\"}\n{\"Name\":\"ad00\",\"Site\":\"abh\"}\n{\"Name\":\"ad01\",\"Site\":\"abh\"}\n{\"Name\":\"ad02\",\"Site\":\"abh\"}\n{\"Name\":\"ad03\",\"Site\":\"abh\"}\n{\"Name\":\"ad04\",\"Site\":\"abh\"}\n{\"Name\":\"ad05\",\"Site\":\"abh\"}\n{\"Name\":\"ad06\",\"Site\":\"abh\"}\n{\"Name\":\"ad07\",\"Site\":\"abh\"}\n{\"Name\":\"ad08\",\"Site\":\"abh\"}\n{\"Name\":\"ad09\",\"Site\":\"abh\"}\n{\"Name\":\"ae00\",\"Site\":\"abh\"}\n{\"Name\":\"ae01\",\"Site\":\"abh\"}\n{\"Name\":\"ae02\",\"Site\":\"abh\"}\n{\"Name\":\"ae03\",\"Site\":\"abh\"}\n{\"Name\":\"ae04\",\"Site\":\"abh\"}\n{\"Name\":\"ae05\",\"Site\":\"abh\"}\n{\"Name\":\"ae06\",\"Site\":\"abh\"}\n{\"Name\":\"ae07\",\"Site\":\"abh\"}\n{\"Name\":\"ae08\",\"Site\":\"abh\"}\n{\"Name\":\"ae09\",\"Site\":\"abh\"}\n{\"Name\":\"aa00\",\"Site\":\"abk\"}\n{\"Name\":\"aa01\",\"Site\":\"abk\"}\n{\"Name\":\"aa02\",\"Site\":\"abk\"}\n{\"Name\":\"aa03\",\"Site\":\"abk\"}\n{\"Name\":\"aa04\",\"Site\":\"abk\"}\n{\"Name\":\"aa05\",\"Site\":\"abk\"}\n{\"Name\":\"aa06\",\"Site\":\"abk\"}\n{\"Name\":\"aa07\",\"Site\":\"abk\"}\n{\"Name\":\"aa08\",\"Site\":\"abk\"}\n{\"Name\":\"aa09\",\"Site\":\"abk\"}\n{\"Name\":\"ab00\",\"Site\":\"abk\"}\n{\"Name\":\"ab01\",\"Site\":\"abk\"}\n{\"Name\":\"ab02\",\"Site\":\"abk\"}\n{\"Name\":\"ab03\",\"Site\":\"abk\"}\n{\"Name\":\"ab04\",\"Site\":\"abk\"}\n{\"Name\":\"ab05\",\"Site\":\"abk\"}\n{\"Name\":\"ab06\",\"Site\":\"abk\"}\n{\"Name\":\"ab07\",\"Site\":\"abk\"}\n{\"Name\":\"ab08\",\"Site\":\"abk\"}\n{\"Name\":\"ab09\",\"Site\":\"abk\"}\n{\"Name\":\"ac00\",\"Site\":\"abk\"}\n{\"Name\":\"ac01\",\"Site\":\"abk\"}\n{\"Name\":\"ac02\",\"Site\":\"abk\"}\n{\"Name\":\"ac03\",\"Site\":\"abk\"}\n{\"Name\":\"ac04\",\"Site\":\"abk\"}\n{\"Name\":\"ac05\",\"Site\":\"abk\"}\n{\"Name\":\"ac06\",\"Site\":\"abk\"}\n{\"Name\":\"ac07\",\"Site\":\"abk\"}\n{\"Name\":\"ac08\",\"Site\":\"abk\"}\n{\"Name\":\"ac09\",\"Site\":\"abk\"}\n{\"Name\":\"ad00\",\"Site\":\"abk\"}\n{\"Name\":\"ad01\",\"Site\":\"abk\"}\n{\"Name\":\"ad02\",\"Site\":\"abk\"}\n{\"Name\":\"ad03\",\"Site\":\"abk\"}\n{\"Name\":\"ad04\",\"Site\":\"abk\"}\n{\"Name\":\"ad05\",\"Site\":\"abk\"}\n{\"Name\":\"ad06\",\"Site\":\"abk\"}\n{\"Name\":\"ad07\",\"Site\":\"abk\"}\n{\"Name\":\"ad08\",\"Site\":\"abk\"}\n{\"Name\":\"ad09\",\"Site\":\"abk\"}\n{\"Name\":\"ae00\",\"Site\":\"abk\"}\n{\"Name\":\"ae01\",\"Site\":\"abk\"}\n{\"Name\":\"ae02\",\"Site\":\"abk\"}\n{\"Name\":\"ae03\",\"Site\":\"abk\"}\n{\"Name\":\"ae04\",\"Site\":\"abk\"}\n{\"Name\":\"ae05\",\"Site\":\"abk\"}\n{\"Name\":\"ae06\",\"Site\":\"abk\"}\n{\"Name\":\"ae07\",\"Site\":\"abk\"}\n{\"Name\":\"ae08\",\"Site\":\"abk\"}\n{\"Name\":\"ae09\",\"Site\":\"abk\"}\n{\"Name\":\"aa00\",\"Site\":\"abn\"}\n{\"Name\":\"aa01\",\"Site\":\"abn\"}\n{\"Name\":\"aa02\",\"Site\":\"abn\"}\n{\"Name\":\"aa03\",\"Site\":\"abn\"}\n{\"Name\":\"aa04\",\"Site\":\"abn\"}\n{\"Name\":\"aa05\",\"Site\":\"abn\"}\n{\"Name\":\"aa06\",\"Site\":\"abn\"}\n{\"Name\":\"aa07\",\"Site\":\"abn\"}\n{\"Name\":\"aa08\",\"Site\":\"abn\"}\n{\"Name\":\"aa09\",\"Site\":\"abn\"}\n{\"Name\":\"ab00\",\"Site\":\"abn\"}\n{\"Name\":\"ab01\",\"Site\":\"abn\"}\n{\"Name\":\"ab02\",\"Site\":\"abn\"}\n{\"Name\":\"ab03\",\"Site\":\"abn\"}\n{\"Name\":\"ab04\",\"Site\":\"abn\"}\n{\"Name\":\"ab05\",\"Site\":\"abn\"}\n{\"Name\":\"ab06\",\"Site\":\"abn\"}\n{\"Name\":\"ab07\",\"Site\":\"abn\"}\n{\"Name\":\"ab08\",\"Site\":\"abn\"}\n{\"Name\":\"ab09\",\"Site\":\"abn\"}\n{\"Name\":\"ac00\",\"Site\":\"abn\"}\n{\"Name\":\"ac01\",\"Site\":\"abn\"}\n{\"Name\":\"ac02\",\"Site\":\"abn\"}\n{\"Name\":\"ac03\",\"Site\":\"abn\"}\n{\"Name\":\"ac04\",\"Site\":\"abn\"}\n{\"Name\":\"ac05\",\"Site\":\"abn\"}\n{\"Name\":\"ac06\",\"Site\":\"abn\"}\n{\"Name\":\"ac07\",\"Site\":\"abn\"}\n{\"Name\":\"ac08\",\"Site\":\"abn\"}\n{\"Name\":\"ac09\",\"Site\":\"abn\"}\n{\"Name\":\"ad00\",\"Site\":\"abn\"}\n{\"Name\":\"ad01\",\"Site\":\"abn\"}\n{\"Name\":\"ad02\",\"Site\":\"abn\"}\n{\"Name\":\"ad03\",\"Site\":\"abn\"}\n{\"Name\":\"ad04\",\"Site\":\"abn\"}\n{\"Name\":\"ad05\",\"Site\":\"abn\"}\n{\"Name\":\"ad06\",\"Site\":\"abn\"}\n{\"Name\":\"ad07\",\"Site\":\"abn\"}\n{\"Name\":\"ad08\",\"Site\":\"abn\"}\n{\"Name\":\"ad09\",\"Site\":\"abn\"}\n{\"Name\":\"ae00\",\"Site\":\"abn\"}\n{\"Name\":\"ae01\",\"Site\":\"abn\"}\n{\"Name\":\"ae02\",\"Site\":\"abn\"}\n{\"Name\":\"ae03\",\"Site\":\"abn\"}\n{\"Name\":\"ae04\",\"Site\":\"abn\"}\n{\"Name\":\"ae05\",\"Site\":\"abn\"}\n{\"Name\":\"ae06\",\"Site\":\"abn\"}\n{\"Name\":\"ae07\",\"Site\":\"abn\"}\n{\"Name\":\"ae08\",\"Site\":\"abn\"}\n{\"Name\":\"ae09\",\"Site\":\"abn\"}\n{\"Name\":\"aa00\",\"Site\":\"abq\"}\n{\"Name\":\"aa01\",\"Site\":\"abq\"}\n{\"Name\":\"aa02\",\"Site\":\"abq\"}\n{\"Name\":\"aa03\",\"Site\":\"abq\"}\n{\"Name\":\"aa04\",\"Site\":\"abq\"}\n{\"Name\":\"aa05\",\"Site\":\"abq\"}\n{\"Name\":\"aa06\",\"Site\":\"abq\"}\n{\"Name\":\"aa07\",\"Site\":\"abq\"}\n{\"Name\":\"aa08\",\"Site\":\"abq\"}\n{\"Name\":\"aa09\",\"Site\":\"abq\"}\n{\"Name\":\"ab00\",\"Site\":\"abq\"}\n{\"Name\":\"ab01\",\"Site\":\"abq\"}\n{\"Name\":\"ab02\",\"Site\":\"abq\"}\n{\"Name\":\"ab03\",\"Site\":\"abq\"}\n{\"Name\":\"ab04\",\"Site\":\"abq\"}\n{\"Name\":\"ab05\",\"Site\":\"abq\"}\n{\"Name\":\"ab06\",\"Site\":\"abq\"}\n{\"Name\":\"ab07\",\"Site\":\"abq\"}\n{\"Name\":\"ab08\",\"Site\":\"abq\"}\n{\"Name\":\"ab09\",\"Site\":\"abq\"}\n{\"Name\":\"ac00\",\"Site\":\"abq\"}\n{\"Name\":\"ac01\",\"Site\":\"abq\"}\n{\"Name\":\"ac02\",\"Site\":\"abq\"}\n{\"Name\":\"ac03\",\"Site\":\"abq\"}\n{\"Name\":\"ac04\",\"Site\":\"abq\"}\n{\"Name\":\"ac05\",\"Site\":\"abq\"}\n{\"Name\":\"ac06\",\"Site\":\"abq\"}\n{\"Name\":\"ac07\",\"Site\":\"abq\"}\n{\"Name\":\"ac08\",\"Site\":\"abq\"}\n{\"Name\":\"ac09\",\"Site\":\"abq\"}\n{\"Name\":\"ad00\",\"Site\":\"abq\"}\n{\"Name\":\"ad01\",\"Site\":\"abq\"}\n{\"Name\":\"ad02\",\"Site\":\"abq\"}\n{\"Name\":\"ad03\",\"Site\":\"abq\"}\n{\"Name\":\"ad04\",\"Site\":\"abq\"}\n{\"Name\":\"ad05\",\"Site\":\"abq\"}\n{\"Name\":\"ad06\",\"Site\":\"abq\"}\n{\"Name\":\"ad07\",\"Site\":\"abq\"}\n{\"Name\":\"ad08\",\"Site\":\"abq\"}\n{\"Name\":\"ad09\",\"Site\":\"abq\"}\n{\"Name\":\"ae00\",\"Site\":\"abq\"}\n{\"Name\":\"ae01\",\"Site\":\"abq\"}\n{\"Name\":\"ae02\",\"Site\":\"abq\"}\n{\"Name\":\"ae03\",\"Site\":\"abq\"}\n{\"Name\":\"ae04\",\"Site\":\"abq\"}\n{\"Name\":\"ae05\",\"Site\":\"abq\"}\n{\"Name\":\"ae06\",\"Site\":\"abq\"}\n{\"Name\":\"ae07\",\"Site\":\"abq\"}\n{\"Name\":\"ae08\",\"Site\":\"abq\"}\n{\"Name\":\"ae09\",\"Site\":\"abq\"}\n{\"Name\":\"aa00\",\"Site\":\"abt\"}\n{\"Name\":\"aa01\",\"Site\":\"abt\"}\n{\"Name\":\"aa02\",\"Site\":\"abt\"}\n{\"Name\":\"aa03\",\"Site\":\"abt\"}\n{\"Name\":\"aa04\",\"Site\":\"abt\"}\n{\"Name\":\"aa05\",\"Site\":\"abt\"}\n{\"Name\":\"aa06\",\"Site\":\"abt\"}\n{\"Name\":\"aa07\",\"Site\":\"abt\"}\n{\"Name\":\"aa08\",\"Site\":\"abt\"}\n{\"Name\":\"aa09\",\"Site\":\"abt\"}\n{\"Name\":\"ab00\",\"Site\":\"abt\"}\n{\"Name\":\"ab01\",\"Site\":\"abt\"}\n{\"Name\":\"ab02\",\"Site\":\"abt\"}\n{\"Name\":\"ab03\",\"Site\":\"abt\"}\n{\"Name\":\"ab04\",\"Site\":\"abt\"}\n{\"Name\":\"ab05\",\"Site\":\"abt\"}\n{\"Name\":\"ab06\",\"Site\":\"abt\"}\n{\"Name\":\"ab07\",\"Site\":\"abt\"}\n{\"Name\":\"ab08\",\"Site\":\"abt\"}\n{\"Name\":\"ab09\",\"Site\":\"abt\"}\n{\"Name\":\"ac00\",\"Site\":\"abt\"}\n{\"Name\":\"ac01\",\"Site\":\"abt\"}\n{\"Name\":\"ac02\",\"Site\":\"abt\"}\n{\"Name\":\"ac03\",\"Site\":\"abt\"}\n{\"Name\":\"ac04\",\"Site\":\"abt\"}\n{\"Name\":\"ac05\",\"Site\":\"abt\"}\n{\"Name\":\"ac06\",\"Site\":\"abt\"}\n{\"Name\":\"ac07\",\"Site\":\"abt\"}\n{\"Name\":\"ac08\",\"Site\":\"abt\"}\n{\"Name\":\"ac09\",\"Site\":\"abt\"}\n{\"Name\":\"ad00\",\"Site\":\"abt\"}\n{\"Name\":\"ad01\",\"Site\":\"abt\"}\n{\"Name\":\"ad02\",\"Site\":\"abt\"}\n{\"Name\":\"ad03\",\"Site\":\"abt\"}\n{\"Name\":\"ad04\",\"Site\":\"abt\"}\n{\"Name\":\"ad05\",\"Site\":\"abt\"}\n{\"Name\":\"ad06\",\"Site\":\"abt\"}\n{\"Name\":\"ad07\",\"Site\":\"abt\"}\n{\"Name\":\"ad08\",\"Site\":\"abt\"}\n{\"Name\":\"ad09\",\"Site\":\"abt\"}\n{\"Name\":\"ae00\",\"Site\":\"abt\"}\n{\"Name\":\"ae01\",\"Site\":\"abt\"}\n{\"Name\":\"ae02\",\"Site\":\"abt\"}\n{\"Name\":\"ae03\",\"Site\":\"abt\"}\n{\"Name\":\"ae04\",\"Site\":\"abt\"}\n{\"Name\":\"ae05\",\"Site\":\"abt\"}\n{\"Name\":\"ae06\",\"Site\":\"abt\"}\n{\"Name\":\"ae07\",\"Site\":\"abt\"}\n{\"Name\":\"ae08\",\"Site\":\"abt\"}\n{\"Name\":\"ae09\",\"Site\":\"abt\"}\n{\"Name\":\"aa00\",\"Site\":\"abw\"}\n{\"Name\":\"aa01\",\"Site\":\"abw\"}\n{\"Name\":\"aa02\",\"Site\":\"abw\"}\n{\"Name\":\"aa03\",\"Site\":\"abw\"}\n{\"Name\":\"aa04\",\"Site\":\"abw\"}\n{\"Name\":\"aa05\",\"Site\":\"abw\"}\n{\"Name\":\"aa06\",\"Site\":\"abw\"}\n{\"Name\":\"aa07\",\"Site\":\"abw\"}\n{\"Name\":\"aa08\",\"Site\":\"abw\"}\n{\"Name\":\"aa09\",\"Site\":\"abw\"}\n{\"Name\":\"ab00\",\"Site\":\"abw\"}\n{\"Name\":\"ab01\",\"Site\":\"abw\"}\n{\"Name\":\"ab02\",\"Site\":\"abw\"}\n{\"Name\":\"ab03\",\"Site\":\"abw\"}\n{\"Name\":\"ab04\",\"Site\":\"abw\"}\n{\"Name\":\"ab05\",\"Site\":\"abw\"}\n{\"Name\":\"ab06\",\"Site\":\"abw\"}\n{\"Name\":\"ab07\",\"Site\":\"abw\"}\n{\"Name\":\"ab08\",\"Site\":\"abw\"}\n{\"Name\":\"ab09\",\"Site\":\"abw\"}\n{\"Name\":\"ac00\",\"Site\":\"abw\"}\n{\"Name\":\"ac01\",\"Site\":\"abw\"}\n{\"Name\":\"ac02\",\"Site\":\"abw\"}\n{\"Name\":\"ac03\",\"Site\":\"abw\"}\n{\"Name\":\"ac04\",\"Site\":\"abw\"}\n{\"Name\":\"ac05\",\"Site\":\"abw\"}\n{\"Name\":\"ac06\",\"Site\":\"abw\"}\n{\"Name\":\"ac07\",\"Site\":\"abw\"}\n{\"Name\":\"ac08\",\"Site\":\"abw\"}\n{\"Name\":\"ac09\",\"Site\":\"abw\"}\n{\"Name\":\"ad00\",\"Site\":\"abw\"}\n{\"Name\":\"ad01\",\"Site\":\"abw\"}\n{\"Name\":\"ad02\",\"Site\":\"abw\"}\n{\"Name\":\"ad03\",\"Site\":\"abw\"}\n{\"Name\":\"ad04\",\"Site\":\"abw\"}\n{\"Name\":\"ad05\",\"Site\":\"abw\"}\n{\"Name\":\"ad06\",\"Site\":\"abw\"}\n{\"Name\":\"ad07\",\"Site\":\"abw\"}\n{\"Name\":\"ad08\",\"Site\":\"abw\"}\n{\"Name\":\"ad09\",\"Site\":\"abw\"}\n{\"Name\":\"ae00\",\"Site\":\"abw\"}\n{\"Name\":\"ae01\",\"Site\":\"abw\"}\n{\"Name\":\"ae02\",\"Site\":\"abw\"}\n{\"Name\":\"ae03\",\"Site\":\"abw\"}\n{\"Name\":\"ae04\",\"Site\":\"abw\"}\n{\"Name\":\"ae05\",\"Site\":\"abw\"}\n{\"Name\":\"ae06\",\"Site\":\"abw\"}\n{\"Name\":\"ae07\",\"Site\":\"abw\"}\n{\"Name\":\"ae08\",\"Site\":\"abw\"}\n{\"Name\":\"ae09\",\"Site\":\"abw\"}\n{\"Name\":\"aa00\",\"Site\":\"abz\"}\n{\"Name\":\"aa01\",\"Site\":\"abz\"}\n{\"Name\":\"aa02\",\"Site\":\"abz\"}\n{\"Name\":\"aa03\",\"Site\":\"abz\"}\n{\"Name\":\"aa04\",\"Site\":\"abz\"}\n{\"Name\":\"aa05\",\"Site\":\"abz\"}\n{\"Name\":\"aa06\",\"Site\":\"abz\"}\n{\"Name\":\"aa07\",\"Site\":\"abz\"}\n{\"Name\":\"aa08\",\"Site\":\"abz\"}\n{\"Name\":\"aa09\",\"Site\":\"abz\"}\n{\"Name\":\"ab00\",\"Site\":\"abz\"}\n{\"Name\":\"ab01\",\"Site\":\"abz\"}\n{\"Name\":\"ab02\",\"Site\":\"abz\"}\n{\"Name\":\"ab03\",\"Site\":\"abz\"}\n{\"Name\":\"ab04\",\"Site\":\"abz\"}\n{\"Name\":\"ab05\",\"Site\":\"abz\"}\n{\"Name\":\"ab06\",\"Site\":\"abz\"}\n{\"Name\":\"ab07\",\"Site\":\"abz\"}\n{\"Name\":\"ab08\",\"Site\":\"abz\"}\n{\"Name\":\"ab09\",\"Site\":\"abz\"}\n{\"Name\":\"ac00\",\"Site\":\"abz\"}\n{\"Name\":\"ac01\",\"Site\":\"abz\"}\n{\"Name\":\"ac02\",\"Site\":\"abz\"}\n{\"Name\":\"ac03\",\"Site\":\"abz\"}\n{\"Name\":\"ac04\",\"Site\":\"abz\"}\n{\"Name\":\"ac05\",\"Site\":\"abz\"}\n{\"Name\":\"ac06\",\"Site\":\"abz\"}\n{\"Name\":\"ac07\",\"Site\":\"abz\"}\n{\"Name\":\"ac08\",\"Site\":\"abz\"}\n{\"Name\":\"ac09\",\"Site\":\"abz\"}\n{\"Name\":\"ad00\",\"Site\":\"abz\"}\n{\"Name\":\"ad01\",\"Site\":\"abz\"}\n{\"Name\":\"ad02\",\"Site\":\"abz\"}\n{\"Name\":\"ad03\",\"Site\":\"abz\"}\n{\"Name\":\"ad04\",\"Site\":\"abz\"}\n{\"Name\":\"ad05\",\"Site\":\"abz\"}\n{\"Name\":\"ad06\",\"Site\":\"abz\"}\n{\"Name\":\"ad07\",\"Site\":\"abz\"}\n{\"Name\":\"ad08\",\"Site\":\"abz\"}\n{\"Name\":\"ad09\",\"Site\":\"abz\"}\n{\"Name\":\"ae00\",\"Site\":\"abz\"}\n{\"Name\":\"ae01\",\"Site\":\"abz\"}\n{\"Name\":\"ae02\",\"Site\":\"abz\"}\n{\"Name\":\"ae03\",\"Site\":\"abz\"}\n{\"Name\":\"ae04\",\"Site\":\"abz\"}\n{\"Name\":\"ae05\",\"Site\":\"abz\"}\n{\"Name\":\"ae06\",\"Site\":\"abz\"}\n{\"Name\":\"ae07\",\"Site\":\"abz\"}\n{\"Name\":\"ae08\",\"Site\":\"abz\"}\n{\"Name\":\"ae09\",\"Site\":\"abz\"}\n{\"Name\":\"aa00\",\"Site\":\"acc\"}\n{\"Name\":\"aa01\",\"Site\":\"acc\"}\n{\"Name\":\"aa02\",\"Site\":\"acc\"}\n{\"Name\":\"aa03\",\"Site\":\"acc\"}\n{\"Name\":\"aa04\",\"Site\":\"acc\"}\n{\"Name\":\"aa05\",\"Site\":\"acc\"}\n{\"Name\":\"aa06\",\"Site\":\"acc\"}\n{\"Name\":\"aa07\",\"Site\":\"acc\"}\n{\"Name\":\"aa08\",\"Site\":\"acc\"}\n{\"Name\":\"aa09\",\"Site\":\"acc\"}\n{\"Name\":\"ab00\",\"Site\":\"acc\"}\n{\"Name\":\"ab01\",\"Site\":\"acc\"}\n{\"Name\":\"ab02\",\"Site\":\"acc\"}\n{\"Name\":\"ab03\",\"Site\":\"acc\"}\n{\"Name\":\"ab04\",\"Site\":\"acc\"}\n{\"Name\":\"ab05\",\"Site\":\"acc\"}\n{\"Name\":\"ab06\",\"Site\":\"acc\"}\n{\"Name\":\"ab07\",\"Site\":\"acc\"}\n{\"Name\":\"ab08\",\"Site\":\"acc\"}\n{\"Name\":\"ab09\",\"Site\":\"acc\"}\n{\"Name\":\"ac00\",\"Site\":\"acc\"}\n{\"Name\":\"ac01\",\"Site\":\"acc\"}\n{\"Name\":\"ac02\",\"Site\":\"acc\"}\n{\"Name\":\"ac03\",\"Site\":\"acc\"}\n{\"Name\":\"ac04\",\"Site\":\"acc\"}\n{\"Name\":\"ac05\",\"Site\":\"acc\"}\n{\"Name\":\"ac06\",\"Site\":\"acc\"}\n{\"Name\":\"ac07\",\"Site\":\"acc\"}\n{\"Name\":\"ac08\",\"Site\":\"acc\"}\n{\"Name\":\"ac09\",\"Site\":\"acc\"}\n{\"Name\":\"ad00\",\"Site\":\"acc\"}\n{\"Name\":\"ad01\",\"Site\":\"acc\"}\n{\"Name\":\"ad02\",\"Site\":\"acc\"}\n{\"Name\":\"ad03\",\"Site\":\"acc\"}\n{\"Name\":\"ad04\",\"Site\":\"acc\"}\n{\"Name\":\"ad05\",\"Site\":\"acc\"}\n{\"Name\":\"ad06\",\"Site\":\"acc\"}\n{\"Name\":\"ad07\",\"Site\":\"acc\"}\n{\"Name\":\"ad08\",\"Site\":\"acc\"}\n{\"Name\":\"ad09\",\"Site\":\"acc\"}\n{\"Name\":\"ae00\",\"Site\":\"acc\"}\n{\"Name\":\"ae01\",\"Site\":\"acc\"}\n{\"Name\":\"ae02\",\"Site\":\"acc\"}\n{\"Name\":\"ae03\",\"Site\":\"acc\"}\n{\"Name\":\"ae04\",\"Site\":\"acc\"}\n{\"Name\":\"ae05\",\"Site\":\"acc\"}\n{\"Name\":\"ae06\",\"Site\":\"acc\"}\n{\"Name\":\"ae07\",\"Site\":\"acc\"}\n{\"Name\":\"ae08\",\"Site\":\"acc\"}\n{\"Name\":\"ae09\",\"Site\":\"acc\"}\n{\"Name\":\"aa00\",\"Site\":\"acf\"}\n{\"Name\":\"aa01\",\"Site\":\"acf\"}\n{\"Name\":\"aa02\",\"Site\":\"acf\"}\n{\"Name\":\"aa03\",\"Site\":\"acf\"}\n{\"Name\":\"aa04\",\"Site\":\"acf\"}\n{\"Name\":\"aa05\",\"Site\":\"acf\"}\n{\"Name\":\"aa06\",\"Site\":\"acf\"}\n{\"Name\":\"aa07\",\"Site\":\"acf\"}\n{\"Name\":\"aa08\",\"Site\":\"acf\"}\n{\"Name\":\"aa09\",\"Site\":\"acf\"}\n{\"Name\":\"ab00\",\"Site\":\"acf\"}\n{\"Name\":\"ab01\",\"Site\":\"acf\"}\n{\"Name\":\"ab02\",\"Site\":\"acf\"}\n{\"Name\":\"ab03\",\"Site\":\"acf\"}\n{\"Name\":\"ab04\",\"Site\":\"acf\"}\n{\"Name\":\"ab05\",\"Site\":\"acf\"}\n{\"Name\":\"ab06\",\"Site\":\"acf\"}\n{\"Name\":\"ab07\",\"Site\":\"acf\"}\n{\"Name\":\"ab08\",\"Site\":\"acf\"}\n{\"Name\":\"ab09\",\"Site\":\"acf\"}\n{\"Name\":\"ac00\",\"Site\":\"acf\"}\n{\"Name\":\"ac01\",\"Site\":\"acf\"}\n{\"Name\":\"ac02\",\"Site\":\"acf\"}\n{\"Name\":\"ac03\",\"Site\":\"acf\"}\n{\"Name\":\"ac04\",\"Site\":\"acf\"}\n{\"Name\":\"ac05\",\"Site\":\"acf\"}\n{\"Name\":\"ac06\",\"Site\":\"acf\"}\n{\"Name\":\"ac07\",\"Site\":\"acf\"}\n{\"Name\":\"ac08\",\"Site\":\"acf\"}\n{\"Name\":\"ac09\",\"Site\":\"acf\"}\n{\"Name\":\"ad00\",\"Site\":\"acf\"}\n{\"Name\":\"ad01\",\"Site\":\"acf\"}\n{\"Name\":\"ad02\",\"Site\":\"acf\"}\n{\"Name\":\"ad03\",\"Site\":\"acf\"}\n{\"Name\":\"ad04\",\"Site\":\"acf\"}\n{\"Name\":\"ad05\",\"Site\":\"acf\"}\n{\"Name\":\"ad06\",\"Site\":\"acf\"}\n{\"Name\":\"ad07\",\"Site\":\"acf\"}\n{\"Name\":\"ad08\",\"Site\":\"acf\"}\n{\"Name\":\"ad09\",\"Site\":\"acf\"}\n{\"Name\":\"ae00\",\"Site\":\"acf\"}\n{\"Name\":\"ae01\",\"Site\":\"acf\"}\n{\"Name\":\"ae02\",\"Site\":\"acf\"}\n{\"Name\":\"ae03\",\"Site\":\"acf\"}\n{\"Name\":\"ae04\",\"Site\":\"acf\"}\n{\"Name\":\"ae05\",\"Site\":\"acf\"}\n{\"Name\":\"ae06\",\"Site\":\"acf\"}\n{\"Name\":\"ae07\",\"Site\":\"acf\"}\n{\"Name\":\"ae08\",\"Site\":\"acf\"}\n{\"Name\":\"ae09\",\"Site\":\"acf\"}\n{\"Name\":\"aa00\",\"Site\":\"aci\"}\n{\"Name\":\"aa01\",\"Site\":\"aci\"}\n{\"Name\":\"aa02\",\"Site\":\"aci\"}\n{\"Name\":\"aa03\",\"Site\":\"aci\"}\n{\"Name\":\"aa04\",\"Site\":\"aci\"}\n{\"Name\":\"aa05\",\"Site\":\"aci\"}\n{\"Name\":\"aa06\",\"Site\":\"aci\"}\n{\"Name\":\"aa07\",\"Site\":\"aci\"}\n{\"Name\":\"aa08\",\"Site\":\"aci\"}\n{\"Name\":\"aa09\",\"Site\":\"aci\"}\n{\"Name\":\"ab00\",\"Site\":\"aci\"}\n{\"Name\":\"ab01\",\"Site\":\"aci\"}\n{\"Name\":\"ab02\",\"Site\":\"aci\"}\n{\"Name\":\"ab03\",\"Site\":\"aci\"}\n{\"Name\":\"ab04\",\"Site\":\"aci\"}\n{\"Name\":\"ab05\",\"Site\":\"aci\"}\n{\"Name\":\"ab06\",\"Site\":\"aci\"}\n{\"Name\":\"ab07\",\"Site\":\"aci\"}\n{\"Name\":\"ab08\",\"Site\":\"aci\"}\n{\"Name\":\"ab09\",\"Site\":\"aci\"}\n{\"Name\":\"ac00\",\"Site\":\"aci\"}\n{\"Name\":\"ac01\",\"Site\":\"aci\"}\n{\"Name\":\"ac02\",\"Site\":\"aci\"}\n{\"Name\":\"ac03\",\"Site\":\"aci\"}\n{\"Name\":\"ac04\",\"Site\":\"aci\"}\n{\"Name\":\"ac05\",\"Site\":\"aci\"}\n{\"Name\":\"ac06\",\"Site\":\"aci\"}\n{\"Name\":\"ac07\",\"Site\":\"aci\"}\n{\"Name\":\"ac08\",\"Site\":\"aci\"}\n{\"Name\":\"ac09\",\"Site\":\"aci\"}\n{\"Name\":\"ad00\",\"Site\":\"aci\"}\n{\"Name\":\"ad01\",\"Site\":\"aci\"}\n{\"Name\":\"ad02\",\"Site\":\"aci\"}\n{\"Name\":\"ad03\",\"Site\":\"aci\"}\n{\"Name\":\"ad04\",\"Site\":\"aci\"}\n{\"Name\":\"ad05\",\"Site\":\"aci\"}\n{\"Name\":\"ad06\",\"Site\":\"aci\"}\n{\"Name\":\"ad07\",\"Site\":\"aci\"}\n{\"Name\":\"ad08\",\"Site\":\"aci\"}\n{\"Name\":\"ad09\",\"Site\":\"aci\"}\n{\"Name\":\"ae00\",\"Site\":\"aci\"}\n{\"Name\":\"ae01\",\"Site\":\"aci\"}\n{\"Name\":\"ae02\",\"Site\":\"aci\"}\n{\"Name\":\"ae03\",\"Site\":\"aci\"}\n{\"Name\":\"ae04\",\"Site\":\"aci\"}\n{\"Name\":\"ae05\",\"Site\":\"aci\"}\n{\"Name\":\"ae06\",\"Site\":\"aci\"}\n{\"Name\":\"ae07\",\"Site\":\"aci\"}\n{\"Name\":\"ae08\",\"Site\":\"aci\"}\n{\"Name\":\"ae09\",\"Site\":\"aci\"}\n{\"Name\":\"aa00\",\"Site\":\"acl\"}\n{\"Name\":\"aa01\",\"Site\":\"acl\"}\n{\"Name\":\"aa02\",\"Site\":\"acl\"}\n{\"Name\":\"aa03\",\"Site\":\"acl\"}\n{\"Name\":\"aa04\",\"Site\":\"acl\"}\n{\"Name\":\"aa05\",\"Site\":\"acl\"}\n{\"Name\":\"aa06\",\"Site\":\"acl\"}\n{\"Name\":\"aa07\",\"Site\":\"acl\"}\n{\"Name\":\"aa08\",\"Site\":\"acl\"}\n{\"Name\":\"aa09\",\"Site\":\"acl\"}\n{\"Name\":\"ab00\",\"Site\":\"acl\"}\n{\"Name\":\"ab01\",\"Site\":\"acl\"}\n{\"Name\":\"ab02\",\"Site\":\"acl\"}\n{\"Name\":\"ab03\",\"Site\":\"acl\"}\n{\"Name\":\"ab04\",\"Site\":\"acl\"}\n{\"Name\":\"ab05\",\"Site\":\"acl\"}\n{\"Name\":\"ab06\",\"Site\":\"acl\"}\n{\"Name\":\"ab07\",\"Site\":\"acl\"}\n{\"Name\":\"ab08\",\"Site\":\"acl\"}\n{\"Name\":\"ab09\",\"Site\":\"acl\"}\n{\"Name\":\"ac00\",\"Site\":\"acl\"}\n{\"Name\":\"ac01\",\"Site\":\"acl\"}\n{\"Name\":\"ac02\",\"Site\":\"acl\"}\n{\"Name\":\"ac03\",\"Site\":\"acl\"}\n{\"Name\":\"ac04\",\"Site\":\"acl\"}\n{\"Name\":\"ac05\",\"Site\":\"acl\"}\n{\"Name\":\"ac06\",\"Site\":\"acl\"}\n{\"Name\":\"ac07\",\"Site\":\"acl\"}\n{\"Name\":\"ac08\",\"Site\":\"acl\"}\n{\"Name\":\"ac09\",\"Site\":\"acl\"}\n{\"Name\":\"ad00\",\"Site\":\"acl\"}\n{\"Name\":\"ad01\",\"Site\":\"acl\"}\n{\"Name\":\"ad02\",\"Site\":\"acl\"}\n{\"Name\":\"ad03\",\"Site\":\"acl\"}\n{\"Name\":\"ad04\",\"Site\":\"acl\"}\n{\"Name\":\"ad05\",\"Site\":\"acl\"}\n{\"Name\":\"ad06\",\"Site\":\"acl\"}\n{\"Name\":\"ad07\",\"Site\":\"acl\"}\n{\"Name\":\"ad08\",\"Site\":\"acl\"}\n{\"Name\":\"ad09\",\"Site\":\"acl\"}\n{\"Name\":\"ae00\",\"Site\":\"acl\"}\n{\"Name\":\"ae01\",\"Site\":\"acl\"}\n{\"Name\":\"ae02\",\"Site\":\"acl\"}\n{\"Name\":\"ae03\",\"Site\":\"acl\"}\n{\"Name\":\"ae04\",\"Site\":\"acl\"}\n{\"Name\":\"ae05\",\"Site\":\"acl\"}\n{\"Name\":\"ae06\",\"Site\":\"acl\"}\n{\"Name\":\"ae07\",\"Site\":\"acl\"}\n{\"Name\":\"ae08\",\"Site\":\"acl\"}\n{\"Name\":\"ae09\",\"Site\":\"acl\"}\n{\"Name\":\"aa00\",\"Site\":\"aco\"}\n{\"Name\":\"aa01\",\"Site\":\"aco\"}\n{\"Name\":\"aa02\",\"Site\":\"aco\"}\n{\"Name\":\"aa03\",\"Site\":\"aco\"}\n{\"Name\":\"aa04\",\"Site\":\"aco\"}\n{\"Name\":\"aa05\",\"Site\":\"aco\"}\n{\"Name\":\"aa06\",\"Site\":\"aco\"}\n{\"Name\":\"aa07\",\"Site\":\"aco\"}\n{\"Name\":\"aa08\",\"Site\":\"aco\"}\n{\"Name\":\"aa09\",\"Site\":\"aco\"}\n{\"Name\":\"ab00\",\"Site\":\"aco\"}\n{\"Name\":\"ab01\",\"Site\":\"aco\"}\n{\"Name\":\"ab02\",\"Site\":\"aco\"}\n{\"Name\":\"ab03\",\"Site\":\"aco\"}\n{\"Name\":\"ab04\",\"Site\":\"aco\"}\n{\"Name\":\"ab05\",\"Site\":\"aco\"}\n{\"Name\":\"ab06\",\"Site\":\"aco\"}\n{\"Name\":\"ab07\",\"Site\":\"aco\"}\n{\"Name\":\"ab08\",\"Site\":\"aco\"}\n{\"Name\":\"ab09\",\"Site\":\"aco\"}\n{\"Name\":\"ac00\",\"Site\":\"aco\"}\n{\"Name\":\"ac01\",\"Site\":\"aco\"}\n{\"Name\":\"ac02\",\"Site\":\"aco\"}\n{\"Name\":\"ac03\",\"Site\":\"aco\"}\n{\"Name\":\"ac04\",\"Site\":\"aco\"}\n{\"Name\":\"ac05\",\"Site\":\"aco\"}\n{\"Name\":\"ac06\",\"Site\":\"aco\"}\n{\"Name\":\"ac07\",\"Site\":\"aco\"}\n{\"Name\":\"ac08\",\"Site\":\"aco\"}\n{\"Name\":\"ac09\",\"Site\":\"aco\"}\n{\"Name\":\"ad00\",\"Site\":\"aco\"}\n{\"Name\":\"ad01\",\"Site\":\"aco\"}\n{\"Name\":\"ad02\",\"Site\":\"aco\"}\n{\"Name\":\"ad03\",\"Site\":\"aco\"}\n{\"Name\":\"ad04\",\"Site\":\"aco\"}\n{\"Name\":\"ad05\",\"Site\":\"aco\"}\n{\"Name\":\"ad06\",\"Site\":\"aco\"}\n{\"Name\":\"ad07\",\"Site\":\"aco\"}\n{\"Name\":\"ad08\",\"Site\":\"aco\"}\n{\"Name\":\"ad09\",\"Site\":\"aco\"}\n{\"Name\":\"ae00\",\"Site\":\"aco\"}\n{\"Name\":\"ae01\",\"Site\":\"aco\"}\n{\"Name\":\"ae02\",\"Site\":\"aco\"}\n{\"Name\":\"ae03\",\"Site\":\"aco\"}\n{\"Name\":\"ae04\",\"Site\":\"aco\"}\n{\"Name\":\"ae05\",\"Site\":\"aco\"}\n{\"Name\":\"ae06\",\"Site\":\"aco\"}\n{\"Name\":\"ae07\",\"Site\":\"aco\"}\n{\"Name\":\"ae08\",\"Site\":\"aco\"}\n{\"Name\":\"ae09\",\"Site\":\"aco\"}\n{\"Name\":\"aa00\",\"Site\":\"acr\"}\n{\"Name\":\"aa01\",\"Site\":\"acr\"}\n{\"Name\":\"aa02\",\"Site\":\"acr\"}\n{\"Name\":\"aa03\",\"Site\":\"acr\"}\n{\"Name\":\"aa04\",\"Site\":\"acr\"}\n{\"Name\":\"aa05\",\"Site\":\"acr\"}\n{\"Name\":\"aa06\",\"Site\":\"acr\"}\n{\"Name\":\"aa07\",\"Site\":\"acr\"}\n{\"Name\":\"aa08\",\"Site\":\"acr\"}\n{\"Name\":\"aa09\",\"Site\":\"acr\"}\n{\"Name\":\"ab00\",\"Site\":\"acr\"}\n{\"Name\":\"ab01\",\"Site\":\"acr\"}\n{\"Name\":\"ab02\",\"Site\":\"acr\"}\n{\"Name\":\"ab03\",\"Site\":\"acr\"}\n{\"Name\":\"ab04\",\"Site\":\"acr\"}\n{\"Name\":\"ab05\",\"Site\":\"acr\"}\n{\"Name\":\"ab06\",\"Site\":\"acr\"}\n{\"Name\":\"ab07\",\"Site\":\"acr\"}\n{\"Name\":\"ab08\",\"Site\":\"acr\"}\n{\"Name\":\"ab09\",\"Site\":\"acr\"}\n{\"Name\":\"ac00\",\"Site\":\"acr\"}\n{\"Name\":\"ac01\",\"Site\":\"acr\"}\n{\"Name\":\"ac02\",\"Site\":\"acr\"}\n{\"Name\":\"ac03\",\"Site\":\"acr\"}\n{\"Name\":\"ac04\",\"Site\":\"acr\"}\n{\"Name\":\"ac05\",\"Site\":\"acr\"}\n{\"Name\":\"ac06\",\"Site\":\"acr\"}\n{\"Name\":\"ac07\",\"Site\":\"acr\"}\n{\"Name\":\"ac08\",\"Site\":\"acr\"}\n{\"Name\":\"ac09\",\"Site\":\"acr\"}\n{\"Name\":\"ad00\",\"Site\":\"acr\"}\n{\"Name\":\"ad01\",\"Site\":\"acr\"}\n{\"Name\":\"ad02\",\"Site\":\"acr\"}\n{\"Name\":\"ad03\",\"Site\":\"acr\"}\n{\"Name\":\"ad04\",\"Site\":\"acr\"}\n{\"Name\":\"ad05\",\"Site\":\"acr\"}\n{\"Name\":\"ad06\",\"Site\":\"acr\"}\n{\"Name\":\"ad07\",\"Site\":\"acr\"}\n{\"Name\":\"ad08\",\"Site\":\"acr\"}\n{\"Name\":\"ad09\",\"Site\":\"acr\"}\n{\"Name\":\"ae00\",\"Site\":\"acr\"}\n{\"Name\":\"ae01\",\"Site\":\"acr\"}\n{\"Name\":\"ae02\",\"Site\":\"acr\"}\n{\"Name\":\"ae03\",\"Site\":\"acr\"}\n{\"Name\":\"ae04\",\"Site\":\"acr\"}\n{\"Name\":\"ae05\",\"Site\":\"acr\"}\n{\"Name\":\"ae06\",\"Site\":\"acr\"}\n{\"Name\":\"ae07\",\"Site\":\"acr\"}\n{\"Name\":\"ae08\",\"Site\":\"acr\"}\n{\"Name\":\"ae09\",\"Site\":\"acr\"}\n{\"Name\":\"aa00\",\"Site\":\"acu\"}\n{\"Name\":\"aa01\",\"Site\":\"acu\"}\n{\"Name\":\"aa02\",\"Site\":\"acu\"}\n{\"Name\":\"aa03\",\"Site\":\"acu\"}\n{\"Name\":\"aa04\",\"Site\":\"acu\"}\n{\"Name\":\"aa05\",\"Site\":\"acu\"}\n{\"Name\":\"aa06\",\"Site\":\"acu\"}\n{\"Name\":\"aa07\",\"Site\":\"acu\"}\n{\"Name\":\"aa08\",\"Site\":\"acu\"}\n{\"Name\":\"aa09\",\"Site\":\"acu\"}\n{\"Name\":\"ab00\",\"Site\":\"acu\"}\n{\"Name\":\"ab01\",\"Site\":\"acu\"}\n{\"Name\":\"ab02\",\"Site\":\"acu\"}\n{\"Name\":\"ab03\",\"Site\":\"acu\"}\n{\"Name\":\"ab04\",\"Site\":\"acu\"}\n{\"Name\":\"ab05\",\"Site\":\"acu\"}\n{\"Name\":\"ab06\",\"Site\":\"acu\"}\n{\"Name\":\"ab07\",\"Site\":\"acu\"}\n{\"Name\":\"ab08\",\"Site\":\"acu\"}\n{\"Name\":\"ab09\",\"Site\":\"acu\"}\n{\"Name\":\"ac00\",\"Site\":\"acu\"}\n{\"Name\":\"ac01\",\"Site\":\"acu\"}\n{\"Name\":\"ac02\",\"Site\":\"acu\"}\n{\"Name\":\"ac03\",\"Site\":\"acu\"}\n{\"Name\":\"ac04\",\"Site\":\"acu\"}\n{\"Name\":\"ac05\",\"Site\":\"acu\"}\n{\"Name\":\"ac06\",\"Site\":\"acu\"}\n{\"Name\":\"ac07\",\"Site\":\"acu\"}\n{\"Name\":\"ac08\",\"Site\":\"acu\"}\n{\"Name\":\"ac09\",\"Site\":\"acu\"}\n{\"Name\":\"ad00\",\"Site\":\"acu\"}\n{\"Name\":\"ad01\",\"Site\":\"acu\"}\n{\"Name\":\"ad02\",\"Site\":\"acu\"}\n{\"Name\":\"ad03\",\"Site\":\"acu\"}\n{\"Name\":\"ad04\",\"Site\":\"acu\"}\n{\"Name\":\"ad05\",\"Site\":\"acu\"}\n{\"Name\":\"ad06\",\"Site\":\"acu\"}\n{\"Name\":\"ad07\",\"Site\":\"acu\"}\n{\"Name\":\"ad08\",\"Site\":\"acu\"}\n{\"Name\":\"ad09\",\"Site\":\"acu\"}\n{\"Name\":\"ae00\",\"Site\":\"acu\"}\n{\"Name\":\"ae01\",\"Site\":\"acu\"}\n{\"Name\":\"ae02\",\"Site\":\"acu\"}\n{\"Name\":\"ae03\",\"Site\":\"acu\"}\n{\"Name\":\"ae04\",\"Site\":\"acu\"}\n{\"Name\":\"ae05\",\"Site\":\"acu\"}\n{\"Name\":\"ae06\",\"Site\":\"acu\"}\n{\"Name\":\"ae07\",\"Site\":\"acu\"}\n{\"Name\":\"ae08\",\"Site\":\"acu\"}\n{\"Name\":\"ae09\",\"Site\":\"acu\"}\n{\"Name\":\"aa00\",\"Site\":\"acx\"}\n{\"Name\":\"aa01\",\"Site\":\"acx\"}\n{\"Name\":\"aa02\",\"Site\":\"acx\"}\n{\"Name\":\"aa03\",\"Site\":\"acx\"}\n{\"Name\":\"aa04\",\"Site\":\"acx\"}\n{\"Name\":\"aa05\",\"Site\":\"acx\"}\n{\"Name\":\"aa06\",\"Site\":\"acx\"}\n{\"Name\":\"aa07\",\"Site\":\"acx\"}\n{\"Name\":\"aa08\",\"Site\":\"acx\"}\n{\"Name\":\"aa09\",\"Site\":\"acx\"}\n{\"Name\":\"ab00\",\"Site\":\"acx\"}\n{\"Name\":\"ab01\",\"Site\":\"acx\"}\n{\"Name\":\"ab02\",\"Site\":\"acx\"}\n{\"Name\":\"ab03\",\"Site\":\"acx\"}\n{\"Name\":\"ab04\",\"Site\":\"acx\"}\n{\"Name\":\"ab05\",\"Site\":\"acx\"}\n{\"Name\":\"ab06\",\"Site\":\"acx\"}\n{\"Name\":\"ab07\",\"Site\":\"acx\"}\n{\"Name\":\"ab08\",\"Site\":\"acx\"}\n{\"Name\":\"ab09\",\"Site\":\"acx\"}\n{\"Name\":\"ac00\",\"Site\":\"acx\"}\n{\"Name\":\"ac01\",\"Site\":\"acx\"}\n{\"Name\":\"ac02\",\"Site\":\"acx\"}\n{\"Name\":\"ac03\",\"Site\":\"acx\"}\n{\"Name\":\"ac04\",\"Site\":\"acx\"}\n{\"Name\":\"ac05\",\"Site\":\"acx\"}\n{\"Name\":\"ac06\",\"Site\":\"acx\"}\n{\"Name\":\"ac07\",\"Site\":\"acx\"}\n{\"Name\":\"ac08\",\"Site\":\"acx\"}\n{\"Name\":\"ac09\",\"Site\":\"acx\"}\n{\"Name\":\"ad00\",\"Site\":\"acx\"}\n{\"Name\":\"ad01\",\"Site\":\"acx\"}\n{\"Name\":\"ad02\",\"Site\":\"acx\"}\n{\"Name\":\"ad03\",\"Site\":\"acx\"}\n{\"Name\":\"ad04\",\"Site\":\"acx\"}\n{\"Name\":\"ad05\",\"Site\":\"acx\"}\n{\"Name\":\"ad06\",\"Site\":\"acx\"}\n{\"Name\":\"ad07\",\"Site\":\"acx\"}\n{\"Name\":\"ad08\",\"Site\":\"acx\"}\n{\"Name\":\"ad09\",\"Site\":\"acx\"}\n{\"Name\":\"ae00\",\"Site\":\"acx\"}\n{\"Name\":\"ae01\",\"Site\":\"acx\"}\n{\"Name\":\"ae02\",\"Site\":\"acx\"}\n{\"Name\":\"ae03\",\"Site\":\"acx\"}\n{\"Name\":\"ae04\",\"Site\":\"acx\"}\n{\"Name\":\"ae05\",\"Site\":\"acx\"}\n{\"Name\":\"ae06\",\"Site\":\"acx\"}\n{\"Name\":\"ae07\",\"Site\":\"acx\"}\n{\"Name\":\"ae08\",\"Site\":\"acx\"}\n{\"Name\":\"ae09\",\"Site\":\"acx\"}\n{\"Name\":\"aa00\",\"Site\":\"ada\"}\n{\"Name\":\"aa01\",\"Site\":\"ada\"}\n{\"Name\":\"aa02\",\"Site\":\"ada\"}\n{\"Name\":\"aa03\",\"Site\":\"ada\"}\n{\"Name\":\"aa04\",\"Site\":\"ada\"}\n{\"Name\":\"aa05\",\"Site\":\"ada\"}\n{\"Name\":\"aa06\",\"Site\":\"ada\"}\n{\"Name\":\"aa07\",\"Site\":\"ada\"}\n{\"Name\":\"aa08\",\"Site\":\"ada\"}\n{\"Name\":\"aa09\",\"Site\":\"ada\"}\n{\"Name\":\"ab00\",\"Site\":\"ada\"}\n{\"Name\":\"ab01\",\"Site\":\"ada\"}\n{\"Name\":\"ab02\",\"Site\":\"ada\"}\n{\"Name\":\"ab03\",\"Site\":\"ada\"}\n{\"Name\":\"ab04\",\"Site\":\"ada\"}\n{\"Name\":\"ab05\",\"Site\":\"ada\"}\n{\"Name\":\"ab06\",\"Site\":\"ada\"}\n{\"Name\":\"ab07\",\"Site\":\"ada\"}\n{\"Name\":\"ab08\",\"Site\":\"ada\"}\n{\"Name\":\"ab09\",\"Site\":\"ada\"}\n{\"Name\":\"ac00\",\"Site\":\"ada\"}\n{\"Name\":\"ac01\",\"Site\":\"ada\"}\n{\"Name\":\"ac02\",\"Site\":\"ada\"}\n{\"Name\":\"ac03\",\"Site\":\"ada\"}\n{\"Name\":\"ac04\",\"Site\":\"ada\"}\n{\"Name\":\"ac05\",\"Site\":\"ada\"}\n{\"Name\":\"ac06\",\"Site\":\"ada\"}\n{\"Name\":\"ac07\",\"Site\":\"ada\"}\n{\"Name\":\"ac08\",\"Site\":\"ada\"}\n{\"Name\":\"ac09\",\"Site\":\"ada\"}\n{\"Name\":\"ad00\",\"Site\":\"ada\"}\n{\"Name\":\"ad01\",\"Site\":\"ada\"}\n{\"Name\":\"ad02\",\"Site\":\"ada\"}\n{\"Name\":\"ad03\",\"Site\":\"ada\"}\n{\"Name\":\"ad04\",\"Site\":\"ada\"}\n{\"Name\":\"ad05\",\"Site\":\"ada\"}\n{\"Name\":\"ad06\",\"Site\":\"ada\"}\n{\"Name\":\"ad07\",\"Site\":\"ada\"}\n{\"Name\":\"ad08\",\"Site\":\"ada\"}\n{\"Name\":\"ad09\",\"Site\":\"ada\"}\n{\"Name\":\"ae00\",\"Site\":\"ada\"}\n{\"Name\":\"ae01\",\"Site\":\"ada\"}\n{\"Name\":\"ae02\",\"Site\":\"ada\"}\n{\"Name\":\"ae03\",\"Site\":\"ada\"}\n{\"Name\":\"ae04\",\"Site\":\"ada\"}\n{\"Name\":\"ae05\",\"Site\":\"ada\"}\n{\"Name\":\"ae06\",\"Site\":\"ada\"}\n{\"Name\":\"ae07\",\"Site\":\"ada\"}\n{\"Name\":\"ae08\",\"Site\":\"ada\"}\n{\"Name\":\"ae09\",\"Site\":\"ada\"}\n{\"Name\":\"aa00\",\"Site\":\"add\"}\n{\"Name\":\"aa01\",\"Site\":\"add\"}\n{\"Name\":\"aa02\",\"Site\":\"add\"}\n{\"Name\":\"aa03\",\"Site\":\"add\"}\n{\"Name\":\"aa04\",\"Site\":\"add\"}\n{\"Name\":\"aa05\",\"Site\":\"add\"}\n{\"Name\":\"aa06\",\"Site\":\"add\"}\n{\"Name\":\"aa07\",\"Site\":\"add\"}\n{\"Name\":\"aa08\",\"Site\":\"add\"}\n{\"Name\":\"aa09\",\"Site\":\"add\"}\n{\"Name\":\"ab00\",\"Site\":\"add\"}\n{\"Name\":\"ab01\",\"Site\":\"add\"}\n{\"Name\":\"ab02\",\"Site\":\"add\"}\n{\"Name\":\"ab03\",\"Site\":\"add\"}\n{\"Name\":\"ab04\",\"Site\":\"add\"}\n{\"Name\":\"ab05\",\"Site\":\"add\"}\n{\"Name\":\"ab06\",\"Site\":\"add\"}\n{\"Name\":\"ab07\",\"Site\":\"add\"}\n{\"Name\":\"ab08\",\"Site\":\"add\"}\n{\"Name\":\"ab09\",\"Site\":\"add\"}\n{\"Name\":\"ac00\",\"Site\":\"add\"}\n{\"Name\":\"ac01\",\"Site\":\"add\"}\n{\"Name\":\"ac02\",\"Site\":\"add\"}\n{\"Name\":\"ac03\",\"Site\":\"add\"}\n{\"Name\":\"ac04\",\"Site\":\"add\"}\n{\"Name\":\"ac05\",\"Site\":\"add\"}\n{\"Name\":\"ac06\",\"Site\":\"add\"}\n{\"Name\":\"ac07\",\"Site\":\"add\"}\n{\"Name\":\"ac08\",\"Site\":\"add\"}\n{\"Name\":\"ac09\",\"Site\":\"add\"}\n{\"Name\":\"ad00\",\"Site\":\"add\"}\n{\"Name\":\"ad01\",\"Site\":\"add\"}\n{\"Name\":\"ad02\",\"Site\":\"add\"}\n{\"Name\":\"ad03\",\"Site\":\"add\"}\n{\"Name\":\"ad04\",\"Site\":\"add\"}\n{\"Name\":\"ad05\",\"Site\":\"add\"}\n{\"Name\":\"ad06\",\"Site\":\"add\"}\n{\"Name\":\"ad07\",\"Site\":\"add\"}\n{\"Name\":\"ad08\",\"Site\":\"add\"}\n{\"Name\":\"ad09\",\"Site\":\"add\"}\n{\"Name\":\"ae00\",\"Site\":\"add\"}\n{\"Name\":\"ae01\",\"Site\":\"add\"}\n{\"Name\":\"ae02\",\"Site\":\"add\"}\n{\"Name\":\"ae03\",\"Site\":\"add\"}\n{\"Name\":\"ae04\",\"Site\":\"add\"}\n{\"Name\":\"ae05\",\"Site\":\"add\"}\n{\"Name\":\"ae06\",\"Site\":\"add\"}\n{\"Name\":\"ae07\",\"Site\":\"add\"}\n{\"Name\":\"ae08\",\"Site\":\"add\"}\n{\"Name\":\"ae09\",\"Site\":\"add\"}\n{\"Name\":\"aa00\",\"Site\":\"adg\"}\n{\"Name\":\"aa01\",\"Site\":\"adg\"}\n{\"Name\":\"aa02\",\"Site\":\"adg\"}\n{\"Name\":\"aa03\",\"Site\":\"adg\"}\n{\"Name\":\"aa04\",\"Site\":\"adg\"}\n{\"Name\":\"aa05\",\"Site\":\"adg\"}\n{\"Name\":\"aa06\",\"Site\":\"adg\"}\n{\"Name\":\"aa07\",\"Site\":\"adg\"}\n{\"Name\":\"aa08\",\"Site\":\"adg\"}\n{\"Name\":\"aa09\",\"Site\":\"adg\"}\n{\"Name\":\"ab00\",\"Site\":\"adg\"}\n{\"Name\":\"ab01\",\"Site\":\"adg\"}\n{\"Name\":\"ab02\",\"Site\":\"adg\"}\n{\"Name\":\"ab03\",\"Site\":\"adg\"}\n{\"Name\":\"ab04\",\"Site\":\"adg\"}\n{\"Name\":\"ab05\",\"Site\":\"adg\"}\n{\"Name\":\"ab06\",\"Site\":\"adg\"}\n{\"Name\":\"ab07\",\"Site\":\"adg\"}\n{\"Name\":\"ab08\",\"Site\":\"adg\"}\n{\"Name\":\"ab09\",\"Site\":\"adg\"}\n{\"Name\":\"ac00\",\"Site\":\"adg\"}\n{\"Name\":\"ac01\",\"Site\":\"adg\"}\n{\"Name\":\"ac02\",\"Site\":\"adg\"}\n{\"Name\":\"ac03\",\"Site\":\"adg\"}\n{\"Name\":\"ac04\",\"Site\":\"adg\"}\n{\"Name\":\"ac05\",\"Site\":\"adg\"}\n{\"Name\":\"ac06\",\"Site\":\"adg\"}\n{\"Name\":\"ac07\",\"Site\":\"adg\"}\n{\"Name\":\"ac08\",\"Site\":\"adg\"}\n{\"Name\":\"ac09\",\"Site\":\"adg\"}\n{\"Name\":\"ad00\",\"Site\":\"adg\"}\n{\"Name\":\"ad01\",\"Site\":\"adg\"}\n{\"Name\":\"ad02\",\"Site\":\"adg\"}\n{\"Name\":\"ad03\",\"Site\":\"adg\"}\n{\"Name\":\"ad04\",\"Site\":\"adg\"}\n{\"Name\":\"ad05\",\"Site\":\"adg\"}\n{\"Name\":\"ad06\",\"Site\":\"adg\"}\n{\"Name\":\"ad07\",\"Site\":\"adg\"}\n{\"Name\":\"ad08\",\"Site\":\"adg\"}\n{\"Name\":\"ad09\",\"Site\":\"adg\"}\n{\"Name\":\"ae00\",\"Site\":\"adg\"}\n{\"Name\":\"ae01\",\"Site\":\"adg\"}\n{\"Name\":\"ae02\",\"Site\":\"adg\"}\n{\"Name\":\"ae03\",\"Site\":\"adg\"}\n{\"Name\":\"ae04\",\"Site\":\"adg\"}\n{\"Name\":\"ae05\",\"Site\":\"adg\"}\n{\"Name\":\"ae06\",\"Site\":\"adg\"}\n{\"Name\":\"ae07\",\"Site\":\"adg\"}\n{\"Name\":\"ae08\",\"Site\":\"adg\"}\n{\"Name\":\"ae09\",\"Site\":\"adg\"}\n{\"Name\":\"aa00\",\"Site\":\"adj\"}\n{\"Name\":\"aa01\",\"Site\":\"adj\"}\n{\"Name\":\"aa02\",\"Site\":\"adj\"}\n{\"Name\":\"aa03\",\"Site\":\"adj\"}\n{\"Name\":\"aa04\",\"Site\":\"adj\"}\n{\"Name\":\"aa05\",\"Site\":\"adj\"}\n{\"Name\":\"aa06\",\"Site\":\"adj\"}\n{\"Name\":\"aa07\",\"Site\":\"adj\"}\n{\"Name\":\"aa08\",\"Site\":\"adj\"}\n{\"Name\":\"aa09\",\"Site\":\"adj\"}\n{\"Name\":\"ab00\",\"Site\":\"adj\"}\n{\"Name\":\"ab01\",\"Site\":\"adj\"}\n{\"Name\":\"ab02\",\"Site\":\"adj\"}\n{\"Name\":\"ab03\",\"Site\":\"adj\"}\n{\"Name\":\"ab04\",\"Site\":\"adj\"}\n{\"Name\":\"ab05\",\"Site\":\"adj\"}\n{\"Name\":\"ab06\",\"Site\":\"adj\"}\n{\"Name\":\"ab07\",\"Site\":\"adj\"}\n{\"Name\":\"ab08\",\"Site\":\"adj\"}\n{\"Name\":\"ab09\",\"Site\":\"adj\"}\n{\"Name\":\"ac00\",\"Site\":\"adj\"}\n{\"Name\":\"ac01\",\"Site\":\"adj\"}\n{\"Name\":\"ac02\",\"Site\":\"adj\"}\n{\"Name\":\"ac03\",\"Site\":\"adj\"}\n{\"Name\":\"ac04\",\"Site\":\"adj\"}\n{\"Name\":\"ac05\",\"Site\":\"adj\"}\n{\"Name\":\"ac06\",\"Site\":\"adj\"}\n{\"Name\":\"ac07\",\"Site\":\"adj\"}\n{\"Name\":\"ac08\",\"Site\":\"adj\"}\n{\"Name\":\"ac09\",\"Site\":\"adj\"}\n{\"Name\":\"ad00\",\"Site\":\"adj\"}\n{\"Name\":\"ad01\",\"Site\":\"adj\"}\n{\"Name\":\"ad02\",\"Site\":\"adj\"}\n{\"Name\":\"ad03\",\"Site\":\"adj\"}\n{\"Name\":\"ad04\",\"Site\":\"adj\"}\n{\"Name\":\"ad05\",\"Site\":\"adj\"}\n{\"Name\":\"ad06\",\"Site\":\"adj\"}\n{\"Name\":\"ad07\",\"Site\":\"adj\"}\n{\"Name\":\"ad08\",\"Site\":\"adj\"}\n{\"Name\":\"ad09\",\"Site\":\"adj\"}\n{\"Name\":\"ae00\",\"Site\":\"adj\"}\n{\"Name\":\"ae01\",\"Site\":\"adj\"}\n{\"Name\":\"ae02\",\"Site\":\"adj\"}\n{\"Name\":\"ae03\",\"Site\":\"adj\"}\n{\"Name\":\"ae04\",\"Site\":\"adj\"}\n{\"Name\":\"ae05\",\"Site\":\"adj\"}\n{\"Name\":\"ae06\",\"Site\":\"adj\"}\n{\"Name\":\"ae07\",\"Site\":\"adj\"}\n{\"Name\":\"ae08\",\"Site\":\"adj\"}\n{\"Name\":\"ae09\",\"Site\":\"adj\"}\n{\"Name\":\"aa00\",\"Site\":\"adm\"}\n{\"Name\":\"aa01\",\"Site\":\"adm\"}\n{\"Name\":\"aa02\",\"Site\":\"adm\"}\n{\"Name\":\"aa03\",\"Site\":\"adm\"}\n{\"Name\":\"aa04\",\"Site\":\"adm\"}\n{\"Name\":\"aa05\",\"Site\":\"adm\"}\n{\"Name\":\"aa06\",\"Site\":\"adm\"}\n{\"Name\":\"aa07\",\"Site\":\"adm\"}\n{\"Name\":\"aa08\",\"Site\":\"adm\"}\n{\"Name\":\"aa09\",\"Site\":\"adm\"}\n{\"Name\":\"ab00\",\"Site\":\"adm\"}\n{\"Name\":\"ab01\",\"Site\":\"adm\"}\n{\"Name\":\"ab02\",\"Site\":\"adm\"}\n{\"Name\":\"ab03\",\"Site\":\"adm\"}\n{\"Name\":\"ab04\",\"Site\":\"adm\"}\n{\"Name\":\"ab05\",\"Site\":\"adm\"}\n{\"Name\":\"ab06\",\"Site\":\"adm\"}\n{\"Name\":\"ab07\",\"Site\":\"adm\"}\n{\"Name\":\"ab08\",\"Site\":\"adm\"}\n{\"Name\":\"ab09\",\"Site\":\"adm\"}\n{\"Name\":\"ac00\",\"Site\":\"adm\"}\n{\"Name\":\"ac01\",\"Site\":\"adm\"}\n{\"Name\":\"ac02\",\"Site\":\"adm\"}\n{\"Name\":\"ac03\",\"Site\":\"adm\"}\n{\"Name\":\"ac04\",\"Site\":\"adm\"}\n{\"Name\":\"ac05\",\"Site\":\"adm\"}\n{\"Name\":\"ac06\",\"Site\":\"adm\"}\n{\"Name\":\"ac07\",\"Site\":\"adm\"}\n{\"Name\":\"ac08\",\"Site\":\"adm\"}\n{\"Name\":\"ac09\",\"Site\":\"adm\"}\n{\"Name\":\"ad00\",\"Site\":\"adm\"}\n{\"Name\":\"ad01\",\"Site\":\"adm\"}\n{\"Name\":\"ad02\",\"Site\":\"adm\"}\n{\"Name\":\"ad03\",\"Site\":\"adm\"}\n{\"Name\":\"ad04\",\"Site\":\"adm\"}\n{\"Name\":\"ad05\",\"Site\":\"adm\"}\n{\"Name\":\"ad06\",\"Site\":\"adm\"}\n{\"Name\":\"ad07\",\"Site\":\"adm\"}\n{\"Name\":\"ad08\",\"Site\":\"adm\"}\n{\"Name\":\"ad09\",\"Site\":\"adm\"}\n{\"Name\":\"ae00\",\"Site\":\"adm\"}\n{\"Name\":\"ae01\",\"Site\":\"adm\"}\n{\"Name\":\"ae02\",\"Site\":\"adm\"}\n{\"Name\":\"ae03\",\"Site\":\"adm\"}\n{\"Name\":\"ae04\",\"Site\":\"adm\"}\n{\"Name\":\"ae05\",\"Site\":\"adm\"}\n{\"Name\":\"ae06\",\"Site\":\"adm\"}\n{\"Name\":\"ae07\",\"Site\":\"adm\"}\n{\"Name\":\"ae08\",\"Site\":\"adm\"}\n{\"Name\":\"ae09\",\"Site\":\"adm\"}\n{\"Name\":\"aa00\",\"Site\":\"adp\"}\n{\"Name\":\"aa01\",\"Site\":\"adp\"}\n{\"Name\":\"aa02\",\"Site\":\"adp\"}\n{\"Name\":\"aa03\",\"Site\":\"adp\"}\n{\"Name\":\"aa04\",\"Site\":\"adp\"}\n{\"Name\":\"aa05\",\"Site\":\"adp\"}\n{\"Name\":\"aa06\",\"Site\":\"adp\"}\n{\"Name\":\"aa07\",\"Site\":\"adp\"}\n{\"Name\":\"aa08\",\"Site\":\"adp\"}\n{\"Name\":\"aa09\",\"Site\":\"adp\"}\n{\"Name\":\"ab00\",\"Site\":\"adp\"}\n{\"Name\":\"ab01\",\"Site\":\"adp\"}\n{\"Name\":\"ab02\",\"Site\":\"adp\"}\n{\"Name\":\"ab03\",\"Site\":\"adp\"}\n{\"Name\":\"ab04\",\"Site\":\"adp\"}\n{\"Name\":\"ab05\",\"Site\":\"adp\"}\n{\"Name\":\"ab06\",\"Site\":\"adp\"}\n{\"Name\":\"ab07\",\"Site\":\"adp\"}\n{\"Name\":\"ab08\",\"Site\":\"adp\"}\n{\"Name\":\"ab09\",\"Site\":\"adp\"}\n{\"Name\":\"ac00\",\"Site\":\"adp\"}\n{\"Name\":\"ac01\",\"Site\":\"adp\"}\n{\"Name\":\"ac02\",\"Site\":\"adp\"}\n{\"Name\":\"ac03\",\"Site\":\"adp\"}\n{\"Name\":\"ac04\",\"Site\":\"adp\"}\n{\"Name\":\"ac05\",\"Site\":\"adp\"}\n{\"Name\":\"ac06\",\"Site\":\"adp\"}\n{\"Name\":\"ac07\",\"Site\":\"adp\"}\n{\"Name\":\"ac08\",\"Site\":\"adp\"}\n{\"Name\":\"ac09\",\"Site\":\"adp\"}\n{\"Name\":\"ad00\",\"Site\":\"adp\"}\n{\"Name\":\"ad01\",\"Site\":\"adp\"}\n{\"Name\":\"ad02\",\"Site\":\"adp\"}\n{\"Name\":\"ad03\",\"Site\":\"adp\"}\n{\"Name\":\"ad04\",\"Site\":\"adp\"}\n{\"Name\":\"ad05\",\"Site\":\"adp\"}\n{\"Name\":\"ad06\",\"Site\":\"adp\"}\n{\"Name\":\"ad07\",\"Site\":\"adp\"}\n{\"Name\":\"ad08\",\"Site\":\"adp\"}\n{\"Name\":\"ad09\",\"Site\":\"adp\"}\n{\"Name\":\"ae00\",\"Site\":\"adp\"}\n{\"Name\":\"ae01\",\"Site\":\"adp\"}\n{\"Name\":\"ae02\",\"Site\":\"adp\"}\n{\"Name\":\"ae03\",\"Site\":\"adp\"}\n{\"Name\":\"ae04\",\"Site\":\"adp\"}\n{\"Name\":\"ae05\",\"Site\":\"adp\"}\n{\"Name\":\"ae06\",\"Site\":\"adp\"}\n{\"Name\":\"ae07\",\"Site\":\"adp\"}\n{\"Name\":\"ae08\",\"Site\":\"adp\"}\n{\"Name\":\"ae09\",\"Site\":\"adp\"}\n{\"Name\":\"aa00\",\"Site\":\"ads\"}\n{\"Name\":\"aa01\",\"Site\":\"ads\"}\n{\"Name\":\"aa02\",\"Site\":\"ads\"}\n{\"Name\":\"aa03\",\"Site\":\"ads\"}\n{\"Name\":\"aa04\",\"Site\":\"ads\"}\n{\"Name\":\"aa05\",\"Site\":\"ads\"}\n{\"Name\":\"aa06\",\"Site\":\"ads\"}\n{\"Name\":\"aa07\",\"Site\":\"ads\"}\n{\"Name\":\"aa08\",\"Site\":\"ads\"}\n{\"Name\":\"aa09\",\"Site\":\"ads\"}\n{\"Name\":\"ab00\",\"Site\":\"ads\"}\n{\"Name\":\"ab01\",\"Site\":\"ads\"}\n{\"Name\":\"ab02\",\"Site\":\"ads\"}\n{\"Name\":\"ab03\",\"Site\":\"ads\"}\n{\"Name\":\"ab04\",\"Site\":\"ads\"}\n{\"Name\":\"ab05\",\"Site\":\"ads\"}\n{\"Name\":\"ab06\",\"Site\":\"ads\"}\n{\"Name\":\"ab07\",\"Site\":\"ads\"}\n{\"Name\":\"ab08\",\"Site\":\"ads\"}\n{\"Name\":\"ab09\",\"Site\":\"ads\"}\n{\"Name\":\"ac00\",\"Site\":\"ads\"}\n{\"Name\":\"ac01\",\"Site\":\"ads\"}\n{\"Name\":\"ac02\",\"Site\":\"ads\"}\n{\"Name\":\"ac03\",\"Site\":\"ads\"}\n{\"Name\":\"ac04\",\"Site\":\"ads\"}\n{\"Name\":\"ac05\",\"Site\":\"ads\"}\n{\"Name\":\"ac06\",\"Site\":\"ads\"}\n{\"Name\":\"ac07\",\"Site\":\"ads\"}\n{\"Name\":\"ac08\",\"Site\":\"ads\"}\n{\"Name\":\"ac09\",\"Site\":\"ads\"}\n{\"Name\":\"ad00\",\"Site\":\"ads\"}\n{\"Name\":\"ad01\",\"Site\":\"ads\"}\n{\"Name\":\"ad02\",\"Site\":\"ads\"}\n{\"Name\":\"ad03\",\"Site\":\"ads\"}\n{\"Name\":\"ad04\",\"Site\":\"ads\"}\n{\"Name\":\"ad05\",\"Site\":\"ads\"}\n{\"Name\":\"ad06\",\"Site\":\"ads\"}\n{\"Name\":\"ad07\",\"Site\":\"ads\"}\n{\"Name\":\"ad08\",\"Site\":\"ads\"}\n{\"Name\":\"ad09\",\"Site\":\"ads\"}\n{\"Name\":\"ae00\",\"Site\":\"ads\"}\n{\"Name\":\"ae01\",\"Site\":\"ads\"}\n{\"Name\":\"ae02\",\"Site\":\"ads\"}\n{\"Name\":\"ae03\",\"Site\":\"ads\"}\n{\"Name\":\"ae04\",\"Site\":\"ads\"}\n{\"Name\":\"ae05\",\"Site\":\"ads\"}\n{\"Name\":\"ae06\",\"Site\":\"ads\"}\n{\"Name\":\"ae07\",\"Site\":\"ads\"}\n{\"Name\":\"ae08\",\"Site\":\"ads\"}\n{\"Name\":\"ae09\",\"Site\":\"ads\"}\n{\"Name\":\"aa00\",\"Site\":\"adv\"}\n{\"Name\":\"aa01\",\"Site\":\"adv\"}\n{\"Name\":\"aa02\",\"Site\":\"adv\"}\n{\"Name\":\"aa03\",\"Site\":\"adv\"}\n{\"Name\":\"aa04\",\"Site\":\"adv\"}\n{\"Name\":\"aa05\",\"Site\":\"adv\"}\n{\"Name\":\"aa06\",\"Site\":\"adv\"}\n{\"Name\":\"aa07\",\"Site\":\"adv\"}\n{\"Name\":\"aa08\",\"Site\":\"adv\"}\n{\"Name\":\"aa09\",\"Site\":\"adv\"}\n{\"Name\":\"ab00\",\"Site\":\"adv\"}\n{\"Name\":\"ab01\",\"Site\":\"adv\"}\n{\"Name\":\"ab02\",\"Site\":\"adv\"}\n{\"Name\":\"ab03\",\"Site\":\"adv\"}\n{\"Name\":\"ab04\",\"Site\":\"adv\"}\n{\"Name\":\"ab05\",\"Site\":\"adv\"}\n{\"Name\":\"ab06\",\"Site\":\"adv\"}\n{\"Name\":\"ab07\",\"Site\":\"adv\"}\n{\"Name\":\"ab08\",\"Site\":\"adv\"}\n{\"Name\":\"ab09\",\"Site\":\"adv\"}\n{\"Name\":\"ac00\",\"Site\":\"adv\"}\n{\"Name\":\"ac01\",\"Site\":\"adv\"}\n{\"Name\":\"ac02\",\"Site\":\"adv\"}\n{\"Name\":\"ac03\",\"Site\":\"adv\"}\n{\"Name\":\"ac04\",\"Site\":\"adv\"}\n{\"Name\":\"ac05\",\"Site\":\"adv\"}\n{\"Name\":\"ac06\",\"Site\":\"adv\"}\n{\"Name\":\"ac07\",\"Site\":\"adv\"}\n{\"Name\":\"ac08\",\"Site\":\"adv\"}\n{\"Name\":\"ac09\",\"Site\":\"adv\"}\n{\"Name\":\"ad00\",\"Site\":\"adv\"}\n{\"Name\":\"ad01\",\"Site\":\"adv\"}\n{\"Name\":\"ad02\",\"Site\":\"adv\"}\n{\"Name\":\"ad03\",\"Site\":\"adv\"}\n{\"Name\":\"ad04\",\"Site\":\"adv\"}\n{\"Name\":\"ad05\",\"Site\":\"adv\"}\n{\"Name\":\"ad06\",\"Site\":\"adv\"}\n{\"Name\":\"ad07\",\"Site\":\"adv\"}\n{\"Name\":\"ad08\",\"Site\":\"adv\"}\n{\"Name\":\"ad09\",\"Site\":\"adv\"}\n{\"Name\":\"ae00\",\"Site\":\"adv\"}\n{\"Name\":\"ae01\",\"Site\":\"adv\"}\n{\"Name\":\"ae02\",\"Site\":\"adv\"}\n{\"Name\":\"ae03\",\"Site\":\"adv\"}\n{\"Name\":\"ae04\",\"Site\":\"adv\"}\n{\"Name\":\"ae05\",\"Site\":\"adv\"}\n{\"Name\":\"ae06\",\"Site\":\"adv\"}\n{\"Name\":\"ae07\",\"Site\":\"adv\"}\n{\"Name\":\"ae08\",\"Site\":\"adv\"}\n{\"Name\":\"ae09\",\"Site\":\"adv\"}\n"
  },
  {
    "path": "chapter/16/workflow/data/packages/sites/sites.go",
    "content": "/*\nPackage sites contains types, functions and methods for reading and interpreting data\nabout sites contained in data files sites.json and machines.json\n\nThis data can be accessed through the global variable \"Data\".\n*/\npackage sites\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n)\n\nvar (\n\tsiteNameRE    = regexp.MustCompile(`[a-z][a-z][a-z]`)\n\tmachineNameRE = regexp.MustCompile(`[a-z][a-z]0[0-9]`)\n)\n\n// Data holds the site data and is how to access the data.\n// Note: in a real system, the data here would always be a copy. That way\n// different things accessing it could not change it for others.\nvar Data SiteData\n\n// Init initializes our Data given the data location. Call from main().\nfunc Init(loc string) {\n\tsd, err := newSiteData(loc)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tData = sd\n}\n\n// Site represents an individual site where we have machines located.\ntype Site struct {\n\t// Name is the name of the site.\n\tName string\n\t// Type is the type of site.\n\tType string\n\t// Status is the status of the site.\n\tStatus string\n\t// Machines are a list of machines in the site.\n\tMachines []Machine\n}\n\n// Validate validates Site's fields are valid.\nfunc (s Site) Validate() error {\n\tif !siteNameRE.MatchString(s.Name) {\n\t\treturn fmt.Errorf(\".Name(%s) is not a valid Site name\", s.Name)\n\t}\n\tswitch s.Type {\n\tcase \"satellite\", \"cluster\":\n\tdefault:\n\t\treturn fmt.Errorf(\"site has .Type(%s) that is invalid\", s.Type)\n\t}\n\tswitch s.Status {\n\tcase \"inService\", \"decom\", \"removed\":\n\tdefault:\n\t\treturn fmt.Errorf(\"site has .Status(%s) that is invalid\", s.Status)\n\t}\n\treturn nil\n}\n\n// Machine represents a physical machine located in a site.\ntype Machine struct {\n\t// Name is the name of the machine.\n\tName string\n\t// Site is the site the machine is located at.\n\tSite string\n}\n\n// FullName retrieves the globally unique name for the machine.\nfunc (m Machine) FullName() string {\n\treturn m.Name + \".\" + m.Site\n}\n\n// Validate validates a Machine's fields.\nfunc (m Machine) Validate() error {\n\tif !machineNameRE.MatchString(m.Name) {\n\t\treturn fmt.Errorf(\".Name(%s) is not a valid Machine name\", m.Name)\n\t}\n\tif !siteNameRE.MatchString(m.Site) {\n\t\treturn fmt.Errorf(\".Site(%s) is not a valid Site name for a Machine to belong to\", m.Site)\n\t}\n\treturn nil\n}\n\n// SiteData contains information on our sites and the machines that are located in those sites.\ntype SiteData struct {\n\t// Sites is mapping of all sites by name.\n\tSites map[string]Site\n\t// Machines is a mapping of all machines by their full name (aa01.aaa).\n\tMachines map[string]Machine\n}\n\n// newSiteData creates a new SiteData instance by reading sites.json and machines.json from \"dir\".\nfunc newSiteData(dir string) (SiteData, error) {\n\tsf, err := os.Open(filepath.Join(dir, \"sites.json\"))\n\tif err != nil {\n\t\treturn SiteData{}, err\n\t}\n\tdefer sf.Close()\n\n\tmf, err := os.Open(filepath.Join(dir, \"machines.json\"))\n\tif err != nil {\n\t\treturn SiteData{}, err\n\t}\n\tdefer mf.Close()\n\n\tsd := SiteData{Sites: map[string]Site{}, Machines: map[string]Machine{}}\n\n\tsitesDec := json.NewDecoder(sf)\n\tfor sitesDec.More() {\n\t\ts := Site{}\n\t\tif err := sitesDec.Decode(&s); err != nil {\n\t\t\treturn SiteData{}, err\n\t\t}\n\t\tif err := s.Validate(); err != nil {\n\t\t\treturn SiteData{}, err\n\t\t}\n\t\tsd.Sites[s.Name] = s\n\t}\n\n\tmachinesDec := json.NewDecoder(mf)\n\tfor machinesDec.More() {\n\t\tm := Machine{}\n\t\tif err := machinesDec.Decode(&m); err != nil {\n\t\t\treturn SiteData{}, err\n\t\t}\n\t\tif err := m.Validate(); err != nil {\n\t\t\treturn SiteData{}, err\n\t\t}\n\t\ts, ok := sd.Sites[m.Site]\n\t\tif !ok {\n\t\t\treturn SiteData{}, fmt.Errorf(\"Machine(%s) has Site(%s) that was not found in our sites.json file\", m.Name, m.Site)\n\t\t}\n\t\ts.Machines = append(s.Machines, m)\n\t\tsd.Sites[m.Site] = s\n\t\tsd.Machines[m.FullName()] = m\n\t}\n\treturn sd, nil\n}\n"
  },
  {
    "path": "chapter/16/workflow/data/sites.json",
    "content": "{\"Name\":\"aaa\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"aab\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"aac\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"aad\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"aae\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"aaf\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"aag\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"aah\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"aai\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"aaj\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"aak\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"aal\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"aam\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"aan\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"aao\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"aap\",\"Type\":\"satellite\",\"Status\":\"decom\"}\n{\"Name\":\"aaq\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"aar\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"aas\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"aat\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"aau\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"aav\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"aaw\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"aax\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"aay\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"aaz\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"aba\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"abb\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"abc\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"abd\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"abe\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"abf\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"abg\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"abh\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"abi\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"abj\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"abk\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"abl\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"abm\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"abn\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"abo\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"abp\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"abq\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"abr\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"abs\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"abt\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"abu\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"abv\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"abw\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"abx\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"aby\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"abz\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"aca\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"acb\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"acc\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"acd\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"ace\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"acf\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"acg\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"ach\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"aci\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"acj\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"ack\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"acl\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"acm\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"acn\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"aco\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"acp\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"acq\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"acr\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"acs\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"act\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"acu\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"acv\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"acw\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"acx\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"acy\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"acz\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"ada\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"adb\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"adc\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"add\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"ade\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"adf\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"adg\",\"Type\":\"satellite\",\"Status\":\"decom\"}\n{\"Name\":\"adh\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"adi\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"adj\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"adk\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"adl\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"adm\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"adn\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"ado\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"adp\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"adq\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"adr\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"ads\",\"Type\":\"satellite\",\"Status\":\"inService\"}\n{\"Name\":\"adt\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"adu\",\"Type\":\"cluster\",\"Status\":\"inService\"}\n{\"Name\":\"adv\",\"Type\":\"satellite\",\"Status\":\"decom\"}\n"
  },
  {
    "path": "chapter/16/workflow/internal/es/es.go",
    "content": "/*\nPackage es contains an emergency stop implementation. This data is read from es.json file\nevery 10 seconds. If the data changes, subscribers will receive an update.\n\nUsing this is simple:\n\tch, cancel := es.Data.Subscribe(\"SatelliteDiskErase\")\n\tdefer cancel()\n\n\tif <-ch != es.Go {\n\t\t// Do something\n\t}\n\n\tselect {\n\tcase <-ch:\n\t\tlog.Println(\"ES changed to Stop state \")\n\t}\n*/\npackage es\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n)\n\n// Data is how to access the emergency stop information.\nvar Data *Reader\n\nfunc init() {\n\td, err := newReader()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tData = d\n}\n\n// Status indicates the emergency stop status.\ntype Status string\n\nconst (\n\t// Unknown means the status was not set.\n\tUnknown Status = \"\"\n\t// Go indicates the matching workflow can execute.\n\tGo Status = \"go\"\n\t// Stop indicates that the matching workflow should not execute and\n\t// existing ones should be stopped.\n\tStop Status = \"stop\"\n)\n\n// Info is the emergency stop information for a particular entry in our es.json file.\ntype Info struct {\n\t// Name is the WorkReq type.\n\tName string\n\t// Status is the emergency stop status.\n\tStatus Status\n}\n\nfunc (i Info) validate() error {\n\ti.Name = strings.TrimSpace(i.Name)\n\tif i.Name == \"\" {\n\t\treturn fmt.Errorf(\"es.json: rule with empty name, ignored\")\n\t}\n\tswitch i.Status {\n\tcase \"go\", \"stop\":\n\tdefault:\n\t\treturn fmt.Errorf(\"es.json: rule(%s) has invalid Status(%s), ignored\", i.Name, i.Status)\n\t}\n\treturn nil\n}\n\n// Reader reads the es.json file at intervals and makes the data and changes to the data\n// available.\ntype Reader struct {\n\tentries atomic.Value // map[string]Info\n\n\tmu          sync.Mutex\n\tsubscribers map[string][]chan Status\n}\n\nfunc newReader() (*Reader, error) {\n\tr := &Reader{subscribers: map[string][]chan Status{}}\n\n\tm, err := r.load()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tr.entries.Store(m)\n\n\tgo r.loop()\n\treturn r, nil\n}\n\n// Cancel is used to cancel your subscription.\ntype Cancel func()\n\n// Subscribe returns a channel that sends a Status whenever a ES entry changes status.\n// This will send the initial Status immediately. If the name is not found, this will\n// send Stop and close the channel. If there is a transition to Stop, the channel will\n// be closed once this is sent. Once you either receive a Stop or are no longer interested\n// in listening, simply call Cancel().\nfunc (r *Reader) Subscribe(name string) (chan Status, Cancel) {\n\ti, ok := r.entries.Load().(map[string]Info)[name]\n\tif !ok || i.Status != Go {\n\t\tch := make(chan Status, 1)\n\t\tch <- Stop\n\t\tclose(ch)\n\t\treturn ch, func() {}\n\t}\n\n\tr.mu.Lock()\n\tdefer r.mu.Unlock()\n\n\tch := make(chan Status, 1)\n\tch <- Go\n\n\tl := r.subscribers[name]\n\tl = append(l, ch)\n\tr.subscribers[name] = l\n\n\t// This removes the channel when it is no longer needed because no\n\t// one is listening.\n\tcancel := func() {\n\t\tr.mu.Lock()\n\t\tdefer r.mu.Unlock()\n\t\tl := make([]chan Status, 0, len(r.subscribers[name])-1)\n\t\tfor _, stored := range r.subscribers[name] {\n\t\t\tif stored == ch {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tl = append(l, stored)\n\t\t}\n\t\tr.subscribers[name] = l\n\t}\n\n\treturn ch, cancel\n}\n\n// Status returns the ES status for the named workflow.\nfunc (r *Reader) Status(name string) Status {\n\tm := r.entries.Load().(map[string]Info)\n\tswitch m[name].Status {\n\tcase Go:\n\t\treturn Go\n\n\t}\n\treturn Stop\n}\n\n// loop reads the es.json file in every 10 seconds and updates subscribers of changes\n// from Go status to Stop status.\nfunc (r *Reader) loop() {\n\tfor _ = range time.Tick(10 * time.Second) {\n\t\tnewInfos, err := r.load()\n\t\tif err != nil {\n\t\t\t// This means the file was malformed or missing. In these\n\t\t\t// cases we stop all work.\n\t\t\tr.mu.Lock()\n\t\t\tfor name := range r.subscribers {\n\t\t\t\tr.sendStop(name)\n\t\t\t}\n\t\t\tr.mu.Unlock()\n\t\t\tcontinue\n\t\t}\n\t\tfor name, info := range r.entries.Load().(map[string]Info) {\n\t\t\tnewInfo, ok := newInfos[name]\n\t\t\tif !ok {\n\t\t\t\tr.sendStop(name)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif info.Status == Go && newInfo.Status != Go {\n\t\t\t\tr.sendStop(name)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tr.entries.Store(newInfos)\n\t}\n}\n\n// sendStop sends a Stop State change to all subscriber to a name.\nfunc (r *Reader) sendStop(name string) {\n\tr.mu.Lock()\n\tdefer r.mu.Unlock()\n\n\tchans := r.subscribers[name]\n\n\tfor _, ch := range chans {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase ch <- Stop:\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\t// If somehow the channel is full, remove the old entry\n\t\t\t\t// and then loop and add the most recent one.\n\t\t\t\tselect {\n\t\t\t\tcase <-ch:\n\t\t\t\tdefault:\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tclose(ch)\n\t\tdelete(r.subscribers, name)\n\t}\n}\n\n// load loads the current es.json values and returns them. Any error is an indication\n// that the file could not be read.\nfunc (r *Reader) load() (map[string]Info, error) {\n\tf, err := os.Open(\"configs/es.json\")\n\tif err != nil {\n\t\treturn map[string]Info{}, fmt.Errorf(\"could not open configs/es.json: %w\", err)\n\t}\n\n\tdec := json.NewDecoder(f)\n\tdec.DisallowUnknownFields()\n\n\tm := map[string]Info{}\n\n\tfor dec.More() {\n\t\tinfo := Info{}\n\t\tif err := dec.Decode(&info); err != nil {\n\t\t\tr.entries.Store(map[string]Info{})\n\t\t\treturn map[string]Info{}, fmt.Errorf(\"es.json file is badly formatted, all jobs moving into stop state\")\n\t\t}\n\t\tif _, ok := m[info.Name]; ok {\n\t\t\tlog.Printf(\"es.json file has two definitions(%s) with the same name, ignoring the second\", info.Name)\n\t\t\tcontinue\n\t\t}\n\t\tm[info.Name] = info\n\t}\n\treturn m, nil\n}\n"
  },
  {
    "path": "chapter/16/workflow/internal/policy/config/config.go",
    "content": "/*\nPackage config stores the policy configuration Go representation, a global variable called\nPolicies that is for reading the Config as it is updated on disk and\nconfiguration validation to make sure errors don't slip in to the Config.\n\nA configuration is stored in JSON and looks like:\n{\n\t\"Name\": \"SateliteDiskErase\",\n\t\"Policies\": [\n\t\t{\n\t\t\t\"Name\": \"restrictJobTypes\",\n\t\t\t\"Settings\": {\n\t\t\t\t\"AllowedJobs\": [\n\t\t\t\t        \"JTValidateDecom\",\n        \t\t\t\t\"JTDiskErase\",\n        \t\t\t\t\"JTSleep\",\n        \t\t\t\t\"JTGetTokenFromBucket\"\n\t\t\t\t]\n\t\t\t}\n\t\t}\n\t]\n}\n...\n*/\npackage config\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/policy\"\n)\n\n// Policies provides the policy Reader that can be used to read the current policy.\nvar Policies *Reader\n\n// Init is called in main to initialize our reads of the policy file. It is called\n// manually instead of init() to guarantee other init() statements are run first.\nfunc Init() {\n\tr, err := newReader(\"configs/policies.json\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tPolicies = r\n}\n\n// Config represents a policy config that is stored on disk.\ntype Config struct {\n\t// Workflows stored by name.\n\tWorkflows map[string]Workflow\n\n\t// Set if the last load of a new Config had errors. This Config will not\n\t// have any errors if this is set.\n\terr error\n}\n\nfunc newConfig() Config {\n\treturn Config{\n\t\tWorkflows: map[string]Workflow{},\n\t}\n}\n\nfunc (c Config) validate() error {\n\tfor k, w := range c.Workflows {\n\t\tif err := w.validate(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tc.Workflows[k] = w // In case there were any changes.\n\t}\n\treturn nil\n}\n\n// Workflow stores a Workflow name and the Policies for that Workflow.\ntype Workflow struct {\n\t// Name is the name of the Workflow.\n\tName string\n\t// Policies are the Policies to be applied to that Workflow.\n\tPolicies []Policy\n}\n\nfunc (w Workflow) validate() error {\n\tw.Name = strings.TrimSpace(w.Name)\n\tif w.Name == \"\" {\n\t\treturn fmt.Errorf(\"Workflow cannot have an empty Name field\")\n\t}\n\n\tfor i, p := range w.Policies {\n\t\tif err := p.validate(); err != nil {\n\t\t\treturn fmt.Errorf(\"Workflow(%s): %s\", w.Name, err)\n\t\t}\n\t\tw.Policies[i] = p // Stores the Policy that has SettingsTyped stored\n\t}\n\treturn nil\n}\n\n// Policy is the policy to apply to a Workflow.\ntype Policy struct {\n\t// Name is the name of the policy type.\n\tName string\n\t// Settings are the settings for that particular policy. We store this as\n\t// a RawMessage so that we can parse it into the typed version on a second pass.\n\tSettings json.RawMessage\n\t// SettingsTyped is the parsed version of the Settings. This is not stored in the Config.\n\tSettingsTyped policy.Settings `json:\"-\"`\n}\n\nfunc (p *Policy) validate() error {\n\tp.Name = strings.TrimSpace(p.Name)\n\tif p.Name == \"\" {\n\t\treturn fmt.Errorf(\"Policy cannot have an empty Name field\")\n\t}\n\ts, err := policy.GetSettings(p.Name)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// This section is going to be confusing, as it is using an advanced topic called\n\t// runtime reflection. This is a topic that really can be its own book. Suffice it to say,\n\t// I wanted every registered implementation of our policy.Settings to be a struct{}, not\n\t// a *struct. This makes it easy to make copies of it without worrying about accidental\n\t// modification. But, you can only unmarshal JSON into a *struct. Because all these specific\n\t// Settings are stored inside an interface called policy.Settings, you can't do:\n\t// &s, because that would be the address of the interface, not the underlying value.\n\t// Confused?? Yeah, I know.......\n\t// So here we are going to use the reflect package to create a pointer to the specific\n\t// value the user put in the interface. Then we are going to unmarshal into that.\n\t// Then we are going to convert that back to a policy.Settings interface.\n\t// Don't spend a bunch of time here, reflection is something better left avoided if you can.\n\tval := reflect.ValueOf(s)\n\tptr := reflect.New(val.Type())\n\n\tif err := json.Unmarshal(p.Settings, ptr.Interface()); err != nil {\n\t\treturn fmt.Errorf(\"policy(%s) could not unmarshal its Settings: %s\", p.Name, err)\n\t}\n\tp.SettingsTyped = ptr.Elem().Interface().(policy.Settings)\n\n\tif err := p.SettingsTyped.Validate(); err != nil {\n\t\treturn fmt.Errorf(\"policy(%s) Settings did not validate: %s\", p.Name, err)\n\t}\n\n\treturn nil\n}\n\n// Reader is used to read the current policy configuration. The\n// configuration is checked for updates every 10 seconds and if there\n// is a valid configuration, it is updated. If not, an error is recorded.\n// Once a Reader is returned by New(), it guarantees to always return a Config.\n// If there is an error also returned, then the Config is the last known good\n// Config.\ntype Reader struct {\n\tloc  string\n\tconf atomic.Value // Config\n}\n\n// Read reads the latest Config we have. If an error is returned, the Config will\n// be valid, but it will be the last known good Config instead of the broken latest\n// Config.\nfunc (r *Reader) Read() (Config, error) {\n\tc := r.conf.Load().(Config)\n\treturn c, c.err\n}\n\nfunc (r *Reader) update() {\n\tfor _ = range time.Tick(10 * time.Second) {\n\t\tif err := r.load(); err != nil {\n\t\t\tc := r.conf.Load().(Config)\n\t\t\tc.err = err\n\t\t\tr.conf.Store(c)\n\t\t}\n\t}\n}\n\nfunc (r *Reader) load() error {\n\tf, err := os.Open(r.loc)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot access policy config(%s): %w\", r.loc, err)\n\t}\n\tdefer f.Close()\n\n\tc := newConfig()\n\n\tdec := json.NewDecoder(f)\n\tdec.DisallowUnknownFields()\n\tfor dec.More() {\n\t\tw := Workflow{}\n\t\tif err := dec.Decode(&w); err != nil {\n\t\t\treturn fmt.Errorf(\"policy on disk could not be JSON decoded: %w\", err)\n\t\t}\n\t\tw.Name = strings.TrimSpace(w.Name)\n\t\tif w.Name == \"\" {\n\t\t\treturn fmt.Errorf(\"Workflow cannot have an empty Name field\")\n\t\t}\n\t\tif _, ok := c.Workflows[w.Name]; ok {\n\t\t\treturn fmt.Errorf(\"cannot have two sets of Workflow policies for %q\", w.Name)\n\t\t}\n\t\tc.Workflows[w.Name] = w\n\t}\n\n\tif err := c.validate(); err != nil {\n\t\treturn fmt.Errorf(\"policy config had an error: %s\", err)\n\t}\n\tr.conf.Store(c)\n\treturn nil\n}\n\n// newReader returns a Reader that can grab the latest Config on disk.\nfunc newReader(loc string) (*Reader, error) {\n\tr := &Reader{loc: loc}\n\tif err := r.load(); err != nil {\n\t\treturn nil, err\n\t}\n\tgo r.update()\n\n\treturn r, nil\n}\n"
  },
  {
    "path": "chapter/16/workflow/internal/policy/policy.go",
    "content": "/*\nPackage policy provides policy primatives, policy registration and functions to run policies against\na WorkReq that is submitted to the system.\n*/\npackage policy\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"google.golang.org/protobuf/proto\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/proto\"\n)\n\nvar policies = map[string]registration{}\n\ntype registration struct {\n\tpolicy   Policy\n\tsettings Settings\n}\n\n// Setting holds a struct that is used to hold the Policy settings that are invoked.\n// These are defined per policy. This should always be a struct and not a *struct.\ntype Settings interface {\n\t// Validate validates the Settings for a Policy.\n\tValidate() error\n}\n\n// Register registers a policy by name with an empty Settings that will be copied\n// to provide the Settings we will read out of the config file.\nfunc Register(name string, p Policy, s Settings) {\n\tname = strings.TrimSpace(name)\n\tif name == \"\" {\n\t\tpanic(\"cannot register a policy with an empty name\")\n\t}\n\n\tif _, ok := policies[name]; ok {\n\t\tpanic(fmt.Sprintf(\"cannot register two policies with the same name(%s)\", name))\n\t}\n\tif p == nil {\n\t\tpanic(\"cannot register a nil policy\")\n\t}\n\tif s == nil {\n\t\tpanic(\"cannot register a policy with a nil setting\")\n\t}\n\tif reflect.ValueOf(s).Kind() != reflect.Struct {\n\t\tpanic(fmt.Sprintf(\"cannot register a policy(%s) with settings that are not a struct\", name))\n\t}\n\tlog.Println(\"Registered Policy: \", name)\n\tpolicies[name] = registration{policy: p, settings: s}\n}\n\n// GetSettings fetches the Settings for a named Policy.\nfunc GetSettings(name string) (Settings, error) {\n\tr, ok := policies[name]\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"policy(%s) cannot be found\", name)\n\t}\n\treturn r.settings, nil\n}\n\n// Policy represents a policy that is defined to check a WorkReq is compliant.\ntype Policy interface {\n\t// Run runs the policy against a request with settings that are specific\n\t// to the policy. settings can be nil for certain implementations.\n\tRun(ctx context.Context, req *pb.WorkReq, settings Settings) error\n}\n\n// PolicyArgs detail a policy and settings to use to invoke it.\ntype PolicyArgs struct {\n\t// Name of the policy in the registry.\n\tName string\n\t// Settings for that policy invocation.\n\tSettings Settings\n}\n\n// Run runs all policies runners that are passed concurrently.\nfunc Run(ctx context.Context, req *pb.WorkReq, args ...PolicyArgs) error {\n\tif len(args) == 0 {\n\t\treturn nil\n\t}\n\n\tvar cancel context.CancelFunc\n\tctx, cancel = context.WithCancel(ctx)\n\tdefer cancel()\n\n\t// Make a deep clone so that no policy is able to make changes.\n\tcreq := proto.Clone(req).(*pb.WorkReq)\n\n\t// Validate that all the policies actually exist and build runners\n\t// to simply run them against the settings in the next part.\n\trunners := make([]func() error, 0, len(args))\n\tfor _, arg := range args {\n\t\targ := arg\n\t\tr, ok := policies[arg.Name]\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"policy(%s) does not exist\", arg.Name)\n\t\t}\n\t\trunners = append(\n\t\t\trunners,\n\t\t\tfunc() error {\n\t\t\t\terr := r.policy.Run(ctx, creq, arg.Settings)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"policy(%s) violation: %w\", arg.Name, err)\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t},\n\t\t)\n\t}\n\n\twg := sync.WaitGroup{}\n\tch := make(chan error, 1)\n\n\t// Run all policy invocations concurrently.\n\twg.Add(len(runners))\n\tfor _, r := range runners {\n\t\tr := r\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tif err := r(); err != nil {\n\t\t\t\tselect {\n\t\t\t\tcase ch <- err:\n\t\t\t\t\tcancel()\n\t\t\t\tdefault:\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t}()\n\t}\n\twg.Wait()\n\n\tselect {\n\tcase err := <-ch:\n\t\treturn err\n\tdefault:\n\t}\n\n\tif !proto.Equal(req, creq) {\n\t\treturn fmt.Errorf(\"a policy tried to modify a request: this is not allowed as it is a security violation\")\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "chapter/16/workflow/internal/policy/register/README",
    "content": "This directory contains policies that are registered with the system.\n\nPolicies can then be assigned to workflow types to put guardrails on a set of work to be performed.\n"
  },
  {
    "path": "chapter/16/workflow/internal/policy/register/restrictjobtypes/restrictjobtypes.go",
    "content": "/*\nPackage restrictjobtypes provides a policy that can be invoked to ensure that a WorkReq only contains\njobs of certain types. Any job outside these types will cause a policy violation.\n*/\npackage restrictjobtypes\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/policy\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/service/jobs\"\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/proto\"\n)\n\n// This registers our policy with the service.\nfunc init() {\n\tp, err := New()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tpolicy.Register(\"restrictJobTypes\", p, Settings{})\n}\n\n// Settings provides settings for a specific implementation of our Policy.\ntype Settings struct {\n\tAllowedJobs []string\n}\n\n// Validate implements policy.Settings.Validate().\nfunc (s Settings) Validate() error {\n\tfor _, n := range s.AllowedJobs {\n\t\t_, err := jobs.GetJob(n)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"allowed job(%s) is not registered in the system\", n)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (s Settings) allowed(name string) bool {\n\tfor _, jn := range s.AllowedJobs {\n\t\tif jn == name {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Policy implements policy.Policy.\ntype Policy struct{}\n\n// New is the constructor for Policy.\nfunc New() (Policy, error) {\n\treturn Policy{}, nil\n}\n\n// Run implements Policy.Run().\nfunc (p Policy) Run(ctx context.Context, req *pb.WorkReq, settings policy.Settings) error {\n\tconst errMsg = \"block(%d)/job(%d) is a type(%s) that is not allowed\"\n\n\ts, ok := settings.(Settings)\n\tif !ok {\n\t\treturn fmt.Errorf(\"settings were not valid type, were %T\", settings)\n\t}\n\n\tfor blockNum, block := range req.Blocks {\n\t\tfor jobNum, job := range block.Jobs {\n\t\t\tif ctx.Err() != nil {\n\t\t\t\treturn ctx.Err()\n\t\t\t}\n\n\t\t\tif !s.allowed(job.Name) {\n\t\t\t\treturn fmt.Errorf(errMsg, blockNum, jobNum, job.Name)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "chapter/16/workflow/internal/policy/register/sameargs/sameargs.go",
    "content": "/*\nPackage sameargs defines a generic policy that can be used to look at Jobs of certain types and\nvalidate that every Job of that type has certain arguments that are the same for every invocation.\n\nThis can be used, for example, to restrict something to working on one service, router, site, region, ...\n*/\npackage sameargs\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/policy\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/service/jobs\"\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/proto\"\n)\n\n// This registers our policy with the service.\nfunc init() {\n\tp, err := New()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tpolicy.Register(\"sameArgs\", p, Settings{})\n}\n\n// ArgKeys are a list of string\ntype ArgKeys []string\n\n// Settings provides settings for a specific implementation of our Policy.\ntype Settings struct {\n\t// Jobs are a list of Job names we want to check and the args\n\t// that must be the same across each Job with that name.\n\tJobs map[string]ArgKeys\n}\n\n// Settings implements policy.Settings.Validate().\nfunc (s Settings) Validate() error {\n\tfor name := range s.Jobs {\n\t\tif _, err := jobs.GetJob(name); err != nil {\n\t\t\treturn fmt.Errorf(\"Job(%s) was not found\", name)\n\t\t}\n\t}\n\treturn nil\n}\n\n// checkJob returns true if we have a setting corresponding to the JobType.\nfunc (s Settings) checkJob(name string) bool {\n\t_, ok := s.Jobs[name]\n\treturn ok\n}\n\n// needKey simply looks at our Jobs argument and determines if we care about\n// a specific arg key for a JobType.\nfunc (s Settings) needKey(name string, k string) bool {\n\tkeys, ok := s.Jobs[name]\n\tif !ok {\n\t\treturn false\n\t}\n\tfor _, key := range keys {\n\t\tif k == key {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// sameCheck holds a mapping of JobType that holds args we care about and\n// the value that should be the same through every instance.\ntype sameCheck map[string]map[string]string\n\n// isSame checks that a key for a JobType has the same value as \"v\". If\n// a value hasn't been stored, it is stored and used on every future check.\nfunc (s sameCheck) isSame(name, k, v string) bool {\n\tkv, ok := s[name]\n\tif !ok {\n\t\ts[name] = map[string]string{k: v}\n\t\treturn true\n\t}\n\n\tstored, ok := kv[k]\n\tif !ok {\n\t\ts[name][k] = v\n\t\treturn true\n\t}\n\tif stored == v {\n\t\treturn true\n\t}\n\treturn false\n}\n\n// Policy implements policy.Policy.\ntype Policy struct{}\n\n// New is the constructor for Polixy.\nfunc New() (Policy, error) {\n\treturn Policy{}, nil\n}\n\n// Run implements Policy.Run().\nfunc (p Policy) Run(ctx context.Context, req *pb.WorkReq, settings policy.Settings) error {\n\ts, ok := settings.(Settings)\n\tif !ok {\n\t\treturn fmt.Errorf(\"settings were not valid type, were %T\", settings)\n\t}\n\n\tsame := sameCheck{}\n\n\tfor blockNum, block := range req.Blocks {\n\t\tfor jobNum, job := range block.Jobs {\n\t\t\tif ctx.Err() != nil {\n\t\t\t\treturn ctx.Err()\n\t\t\t}\n\t\t\tif !s.checkJob(job.Name) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif err := p.argSame(s, job, same, blockNum, jobNum); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (p Policy) argSame(settings Settings, job *pb.Job, same sameCheck, blockNum, jobNum int) error {\n\tconst policyErrMsg = \"block(%d)/job(%d) violated rule: setting(%s) is different for this job\"\n\n\tfor k, v := range job.Args {\n\t\tif settings.needKey(job.Name, k) { // Only check if we care about the key\n\t\t\tif !same.isSame(job.Name, k, v) {\n\t\t\t\treturn fmt.Errorf(policyErrMsg, blockNum, jobNum, k)\n\t\t\t}\n\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "chapter/16/workflow/internal/policy/register/startorend/startOrEnd.go",
    "content": "/*\nPackage startorend implements a policy that can be used to check that a WorkReq\nhas a particular Job in the first block or the last block with certain settings. Inaddition it can allow for certain other jobs to be before of after it (depending on other settings).\n\nThis is useful when you need certain cleanup Jobs, health checks or init jobs to be present.\n*/\npackage startorend\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/policy\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/service/jobs\"\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/proto\"\n)\n\n// This registers our policy with the service.\nfunc init() {\n\tp, err := New()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tpolicy.Register(\"startOrEnd\", p, Settings{})\n}\n\n// Settings provides settings for a specific implementation of our Policy.\ntype Settings struct {\n\t// JobName is the name of the job that must be present.\n\tJobName string\n\t// MustArgs indicate arguments that must be set to a certain setting.\n\tMustArgs map[string]string\n\t// Start indicates this must be one of the first jobs.\n\tStart bool\n\t// End indicates this must be one of the end jobs.\n\tEnd bool\n\t// AllowedBeforeOrAfter are job types that are allowed before\n\t// this job if Start == true, or after this job if End == true.\n\tAllowedBeforeOrAfter []string\n\n\tallowed map[string]bool\n}\n\nfunc (s Settings) Validate() error {\n\tif _, err := jobs.GetJob(s.JobName); err != nil {\n\t\treturn fmt.Errorf(\"Job(%s) is invalid\", s.JobName)\n\t}\n\n\tif s.Start && s.End {\n\t\treturn fmt.Errorf(\"Start and End cannot both be true\")\n\t}\n\tif !s.Start && !s.End {\n\t\treturn fmt.Errorf(\"either Start of End must be set\")\n\t}\n\n\tfor _, name := range s.AllowedBeforeOrAfter {\n\t\tif _, err := jobs.GetJob(name); err != nil {\n\t\t\treturn fmt.Errorf(\"AllowedBeforeOrAfter had Job(%s) that is invalid\", name)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (s Settings) compile() Settings {\n\ts.allowed = map[string]bool{}\n\tfor _, name := range s.AllowedBeforeOrAfter {\n\t\ts.allowed[name] = true\n\t}\n\treturn s\n}\n\n// Policy implements policy.Policy.\ntype Policy struct {\n}\n\n// New is the constructor for Policy.\nfunc New() (Policy, error) {\n\treturn Policy{}, nil\n}\n\n// Run implements Policy.Run().\nfunc (p Policy) Run(ctx context.Context, req *pb.WorkReq, settings policy.Settings) error {\n\ts, ok := settings.(Settings)\n\tif !ok {\n\t\treturn fmt.Errorf(\"settings were not valid type, were %T\", settings)\n\t}\n\n\treturn p.eachWorkReq(ctx, req, s.compile())\n}\n\nfunc (p Policy) eachWorkReq(ctx context.Context, req *pb.WorkReq, s Settings) error {\n\tif s.Start {\n\t\terr := p.startOfBlock(ctx, req.Blocks[0].Jobs, s)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"requires Job(%s) in the first block: %s\", s.JobName, err)\n\t\t}\n\t\treturn err\n\t}\n\n\terr := p.endOfBlock(ctx, req.Blocks[len(req.Blocks)-1].Jobs, s)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"requires Job(%s) in the last block: %s\", s.JobName, err)\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (p Policy) startOfBlock(ctx context.Context, block []*pb.Job, s Settings) error {\n\tfor _, job := range block {\n\t\tif job.Name == s.JobName {\n\t\t\treturn p.mustHave(ctx, job, s)\n\t\t}\n\t\tif s.allowed[job.Name] {\n\t\t\tcontinue\n\t\t}\n\t\treturn fmt.Errorf(\"not found at the beginning of the block\")\n\t}\n\treturn fmt.Errorf(\"not found in the block at all\")\n}\n\nfunc (p Policy) endOfBlock(ctx context.Context, block []*pb.Job, s Settings) error {\n\tfor i := len(block); i > 0; i-- {\n\t\tjob := block[i]\n\t\tif job.Name == s.JobName {\n\t\t\treturn p.mustHave(ctx, job, s)\n\t\t}\n\t\tif s.allowed[job.Name] {\n\t\t\tcontinue\n\t\t}\n\t\treturn fmt.Errorf(\"not found at the beginning of the block\")\n\t}\n\treturn fmt.Errorf(\"not found in the block at all\")\n}\n\nfunc (p Policy) mustHave(ctx context.Context, job *pb.Job, s Settings) error {\n\tfor k, v := range s.MustArgs {\n\t\thas, ok := job.Args[k]\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"found, but required arg(%s) was not found\", k)\n\t\t}\n\t\tif v != has {\n\t\t\treturn fmt.Errorf(\"found,but required  arg(%s) found has incorrect value(got %q, want %q)\", k, has, v)\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "chapter/16/workflow/internal/service/executor/executor.go",
    "content": "/*\nPackage executor provides the Work type which is used to execute a pb.WorkReq.\nThis package is the meat of the engine.\n\nTo create a Work object, simply:\n\twork := executor.New(req, status}\n\nAfter creating a Work object, validate it:\n\tif err := work.Validate(); err !=nil {\n\t\t// Do something\n\t}\n\nTo run the Work object, do:\n\tch := work.Run()\n\nOnce Run() returns, the pb.Status object passed will contain the results of running the WorkReq.\n*/\npackage executor\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/es\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/policy\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/policy/config\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/service/jobs\"\n\t\"google.golang.org/protobuf/proto\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/proto\"\n)\n\n// Work is an executor for executing a WorkReq received by the server.\ntype Work struct {\n\treq *pb.WorkReq\n\n\tmu     sync.Mutex\n\tstatus *pb.StatusResp\n\tch     chan *pb.StatusResp\n}\n\n// New is the constructor for Work.\nfunc New(req *pb.WorkReq, status *pb.StatusResp) *Work {\n\treturn &Work{\n\t\treq:    req,\n\t\tstatus: status,\n\t\tch:     make(chan *pb.StatusResp, 1),\n\t}\n}\n\n// Run validates that a WorkReq is correct and passed policy, then executes it.\nfunc (w *Work) Run(ctx context.Context) chan *pb.StatusResp {\n\tw.setWorkStatus(pb.Status_StatusRunning, false)\n\n\tgo func() {\n\t\tdefer close(w.ch)\n\n\t\tesCh, cancelES := es.Data.Subscribe(w.req.Name)\n\t\tdefer cancelES()\n\n\t\tif <-esCh != es.Go {\n\t\t\tw.setWorkStatus(pb.Status_StatusFailed, true)\n\t\t\treturn\n\t\t}\n\n\t\tvar cancel context.CancelFunc\n\t\tctx, cancel = context.WithCancel(ctx)\n\t\tdefer cancel()\n\n\t\t// If we get an emergency stop, cancel our context.\n\t\t// If the context gets cancelled, then just exit.\n\t\tgo func() {\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\treturn\n\t\t\tcase <-esCh:\n\t\t\t\tlog.Println(\"Emergency Stop called on running workflow type \", w.req.Name)\n\t\t\t\tw.setWorkStatus(pb.Status_StatusFailed, true)\n\t\t\t\tcancel()\n\t\t\t}\n\t\t}()\n\n\t\t// Loop through each block one at a time and execute the Jobs located in them\n\t\t// at the rate limit defined for the block.\n\t\tfor i, block := range w.req.Blocks {\n\t\t\tif ctx.Err() != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tstat := w.status.Blocks[i]\n\n\t\t\tif err := w.runJobs(ctx, block, stat); err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\t// Record our final state based on if any of our blocks failed.\n\t\tcompleted := true\n\t\tfor _, block := range w.status.Blocks {\n\t\t\tif block.Status == pb.Status_StatusFailed {\n\t\t\t\tcompleted = false\n\t\t\t\tw.setWorkStatus(pb.Status_StatusFailed, false)\n\t\t\t}\n\t\t}\n\t\tif completed {\n\t\t\tw.setWorkStatus(pb.Status_StatusCompleted, false)\n\t\t}\n\t}()\n\n\treturn w.ch\n}\n\nfunc (w *Work) setWorkStatus(status pb.Status, esStopped bool) {\n\tw.mu.Lock()\n\tw.status.Status = status\n\tw.status.WasEsStopped = esStopped\n\tw.sendStatus(w.status)\n\tw.mu.Unlock()\n}\n\nfunc (w *Work) setBlockStatus(block *pb.BlockStatus, status pb.Status) {\n\tw.mu.Lock()\n\tblock.Status = status\n\tw.sendStatus(w.status)\n\tw.mu.Unlock()\n}\n\nfunc (w *Work) setJobStatus(job *pb.JobStatus, status pb.Status, err string) {\n\tw.mu.Lock()\n\tjob.Status = status\n\tjob.Error = err\n\tw.sendStatus(w.status)\n\tw.mu.Unlock()\n}\n\n// sendStatus sends the status of the WorkReq on our output channel. If the channel\n// is currently blocked with another status update, it removes that update for the newer one.\nfunc (w *Work) sendStatus(status *pb.StatusResp) {\n\t// We clone our status to prevent any concurrent access issues once the lock around\n\t// sendStatus is released.\n\tstatus = proto.Clone(status).(*pb.StatusResp)\n\tfor {\n\t\tselect {\n\t\tcase w.ch <- status:\n\t\t\treturn\n\t\tdefault:\n\t\t\tselect {\n\t\t\tcase <-w.ch:\n\t\t\tdefault:\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (w *Work) runJobs(ctx context.Context, block *pb.Block, blockStatus *pb.BlockStatus) error {\n\tctx, cancel := context.WithCancel(ctx)\n\n\t// Setup our rate limiter.\n\tlimit := block.RateLimit\n\tif limit < 1 {\n\t\tlimit = 1\n\t}\n\trateLimiter := make(chan struct{}, int(limit))\n\n\tw.setBlockStatus(blockStatus, pb.Status_StatusRunning)\n\n\t// Execute our Jobs.\n\twg := sync.WaitGroup{}\n\tfor i, job := range block.Jobs {\n\t\ti := i\n\t\tjob := job\n\n\t\tselect {\n\t\tcase rateLimiter <- struct{}{}:\n\t\tcase <-ctx.Done():\n\t\t}\n\t\tif ctx.Err() != nil {\n\t\t\tbreak\n\t\t}\n\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tdefer func() { <-rateLimiter }()\n\n\t\t\tjs := blockStatus.Jobs[i]\n\t\t\tj, err := jobs.GetJob(job.Name)\n\t\t\tif err != nil {\n\t\t\t\tcancel()\n\t\t\t\tw.setJobStatus(js, pb.Status_StatusFailed, fmt.Sprintf(\"a Job(%s) passed validation but when ran could not be found, bug?\", job.Name))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tw.setJobStatus(js, pb.Status_StatusRunning, \"\")\n\t\t\terr = j.Run(ctx, job)\n\t\t\tif err != nil {\n\t\t\t\tif jobs.IsFatal(err) {\n\t\t\t\t\tcancel()\n\t\t\t\t}\n\t\t\t\tw.setJobStatus(js, pb.Status_StatusFailed, err.Error())\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tw.setJobStatus(js, pb.Status_StatusCompleted, \"\")\n\t\t}()\n\t}\n\n\twg.Wait()\n\n\t// If any Job failed, the block failed.\n\tfor _, js := range blockStatus.Jobs {\n\t\tif js.Status == pb.Status_StatusFailed {\n\t\t\tw.setBlockStatus(blockStatus, pb.Status_StatusFailed)\n\t\t\treturn ctx.Err()\n\t\t}\n\t}\n\tw.setBlockStatus(blockStatus, pb.Status_StatusCompleted)\n\treturn ctx.Err()\n}\n\n// Validate validates that a WorkReq is valid. This will check that basic values are set correctly\n// and run all policies for this Workflow.\nfunc Validate(ctx context.Context, req *pb.WorkReq) error {\n\tfor blockNum, b := range req.Blocks {\n\t\tif len(b.Jobs) == 0 {\n\t\t\treturn fmt.Errorf(\"Block(%d) had 0 jobs\", blockNum)\n\t\t}\n\t\tfor jobNum, j := range b.Jobs {\n\t\t\tjob, err := jobs.GetJob(j.Name)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"Block(%d) Job(%d) had a invalid Type(%s)\", blockNum, jobNum, j.Name)\n\t\t\t}\n\t\t\tif err := job.Validate(j); err != nil {\n\t\t\t\treturn fmt.Errorf(\"Block(%d) Job(%d)(%s) did not validate: %s)\", blockNum, jobNum, j.Name, err)\n\t\t\t}\n\t\t}\n\t}\n\n\tconf, err := config.Policies.Read()\n\tif err != nil {\n\t\tlog.Println(\"policy config could not be read: \", err)\n\t\treturn fmt.Errorf(\"cannot read our policies config: %s\", err)\n\t}\n\tworkConf, ok := conf.Workflows[req.Name]\n\tif !ok {\n\t\treturn fmt.Errorf(\"Workflow does not have an associated policy in the policy configuration file\")\n\t}\n\n\targs := make([]policy.PolicyArgs, 0, len(workConf.Policies))\n\tfor _, p := range workConf.Policies {\n\t\targs = append(args, policy.PolicyArgs{Name: p.Name, Settings: p.SettingsTyped})\n\t}\n\n\tpolicyContext, cancel := context.WithTimeout(ctx, 30*time.Second)\n\tdefer cancel()\n\n\tif err := policy.Run(policyContext, req, args...); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "chapter/16/workflow/internal/service/jobs/jobs.go",
    "content": "/*\nPackage jobs defines our Job type, which executes work and a registration system for registering\nJobs.\n\nPackages that contain jobs can register themselves by doing:\n\tfunc init() {\n\t\tjobs.Register(\"name\", job)\n\t}\nIf there is a duplicate name, this will panic.\n\nFetching a Job is simply:\n\tGetJob(jt string) (Job, error)\n*/\npackage jobs\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"strings\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/proto\"\n)\n\nvar jobs = map[string]Job{}\n\n// Register registers a Job so that it can be executed.\nfunc Register(name string, job Job) {\n\tname = strings.TrimSpace(name)\n\tif name == \"\" {\n\t\tpanic(\"cannot Register empty JobType\")\n\t}\n\tif _, ok := jobs[name]; ok {\n\t\tpanic(fmt.Sprintf(\"cannot register Job(%s) twice\", name))\n\t}\n\tlog.Println(\"Registered Job: \", name)\n\tjobs[name] = job\n}\n\n// GetJob returns a Job by its type from the registry.\nfunc GetJob(name string) (Job, error) {\n\tj, ok := jobs[name]\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"Job(%v) not found\", name)\n\t}\n\treturn j, nil\n}\n\n// FatalErr is a an error that should terminate a Workflow.\ntype FatalErr struct {\n\terr error\n}\n\n// Fatalf creates a fatal error similar to fmt.Errorf().\nfunc Fatalf(msg string, a ...interface{}) FatalErr {\n\treturn FatalErr{err: fmt.Errorf(msg, a...)}\n}\n\n// IsFatal indicates if an error is fatal.\nfunc IsFatal(err error) bool {\n\treturn errors.Is(err, FatalErr{})\n}\n\n// Is implements the built in Is method.\nfunc (f FatalErr) Is(target error) bool {\n\tswitch target.(type) {\n\tcase FatalErr, *FatalErr:\n\t\treturn true\n\t}\n\treturn false\n}\n\n// Error() implements error.Error().\nfunc (f FatalErr) Error() string {\n\tif f.err == nil {\n\t\treturn \"\"\n\t}\n\treturn f.err.Error()\n}\n\n// Unwrap implements the built in Unwrap method.\nfunc (f FatalErr) Unwrap() error {\n\treturn errors.Unwrap(f.err)\n}\n\n// Job executes some type of work.\ntype Job interface {\n\t// Validate validates that the Job settings sent to the server are valid.\n\tValidate(job *pb.Job) error\n\t// Run runs the Job settings.\n\tRun(ctx context.Context, job *pb.Job) error\n}\n"
  },
  {
    "path": "chapter/16/workflow/internal/service/jobs/register/diskerase/diskerase.go",
    "content": "/*\nPackage diskerase registers a job that can be used to erase a disk on a machine. As\nthis is just a demo, this really just sleeps for 30 seconds.\n\nRegister name: \"diskErase\"\nArgs:\n\t\"machine\"(mandatory): The name of the machine, like \"aa01\" or \"ab02\"\n\t\"site\"(mandatory): The name of the site, like \"aaa\" or \"aba\"\nResult:\n\tErases a disk on a machine, except this is a demo, so it really just sleeps for 30 seconds.\n*/\npackage diskerase\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/data/packages/sites\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/service/jobs\"\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/proto\"\n)\n\n// This registers our Job on server startup.\nfunc init() {\n\tjobs.Register(\"diskErase\", newJob())\n}\n\ntype args struct {\n\tmachine string\n\tsite    string\n}\n\nfunc (a *args) validate(args map[string]string) error {\n\tmust := map[string]bool{\n\t\t\"machine\": false,\n\t\t\"site\":    false,\n\t}\n\n\tfor k, v := range args {\n\t\tswitch k {\n\t\tcase \"machine\":\n\t\t\tmust[\"machine\"] = true\n\t\t\ta.machine = v\n\t\tcase \"site\":\n\t\t\tif _, ok := sites.Data.Sites[v]; !ok {\n\t\t\t\treturn fmt.Errorf(\"site(%s) arg was not a valid site\", v)\n\t\t\t}\n\t\t\tmust[\"site\"] = true\n\t\t\ta.site = v\n\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"invalid arg(%s)\", k)\n\t\t}\n\t}\n\n\tfor k, v := range must {\n\t\tif !v {\n\t\t\treturn fmt.Errorf(\"missing required arg(%s)\", k)\n\t\t}\n\t}\n\n\tfullName := fmt.Sprintf(\"%s.%s\", a.machine, a.site)\n\t_, ok := sites.Data.Machines[fullName]\n\tif !ok {\n\t\treturn fmt.Errorf(\"invalid arg(machine): machine(%s) does not exist\", fullName)\n\t}\n\n\treturn nil\n}\n\n// Job implements jobs.Job.\ntype Job struct {\n\targs args\n}\n\nfunc newJob() *Job {\n\treturn &Job{}\n}\n\n// Validate implements jobs.Job.Validate().\nfunc (j *Job) Validate(job *pb.Job) error {\n\ta := args{}\n\tif err := a.validate(job.Args); err != nil {\n\t\treturn err\n\t}\n\tj.args = a\n\treturn nil\n}\n\n// Run implements jobs.Job.Run().\nfunc (j *Job) Run(ctx context.Context, job *pb.Job) error {\n\ttime.Sleep(30 * time.Second) // A crude and inaccurate simulation of a disk erasure\n\treturn nil\n}\n"
  },
  {
    "path": "chapter/16/workflow/internal/service/jobs/register/sleep/sleep.go",
    "content": "/*\nPackage validatedecom registers a job that is used to validate a site is set to state \"decom\".\n\nRegister name: \"validateDecom\"\nArgs:\n\t\"site\"(mandatory): The name of the site, like \"aaa\" or \"aba\"\n\t\"siteType\"(mandatory): The type of the site, like \"satellite\" or \"cluster\"\nResult:\n\tIf the site is not in decom, will return a fatal error.\n*/\npackage sleep\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/data/packages/sites\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/service/jobs\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/proto\"\n)\n\n// This registers our Job on server startup.\nfunc init() {\n\tjobs.Register(\"sleep\", newJob())\n}\n\ntype args struct {\n\td time.Duration\n}\n\nfunc (a *args) validate(args map[string]string) error {\n\tmust := map[string]bool{\n\t\t\"seconds\": false,\n\t}\n\n\tfor k, v := range args {\n\t\tswitch k {\n\t\tcase \"seconds\":\n\t\t\ti, err := strconv.Atoi(v)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"arg(seconds) is not an integer(%s)\", v)\n\t\t\t}\n\t\t\tif i < 1 {\n\t\t\t\treturn fmt.Errorf(\"arg(seconds) cannot be less than 1(%d)\", i)\n\t\t\t}\n\t\t\tmust[\"seconds\"] = true\n\t\t\ta.d = time.Duration(i) * time.Second\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"validateDecom had invalid arg(%s)\", k)\n\t\t}\n\t}\n\n\tfor k, v := range must {\n\t\tif !v {\n\t\t\treturn fmt.Errorf(\"missing required arg(%s)\", k)\n\t\t}\n\t}\n\treturn nil\n}\n\n// Job implements jobs.Job.\ntype Job struct {\n\tsites map[string]sites.Site\n\targs  args\n}\n\nfunc newJob() *Job {\n\treturn &Job{}\n}\n\n// Validate implements jobs.Job.Validate().\nfunc (j *Job) Validate(job *pb.Job) error {\n\ta := args{}\n\tif err := a.validate(job.Args); err != nil {\n\t\treturn err\n\t}\n\tj.args = a\n\treturn nil\n}\n\n// Run implements jobs.Job.Run().\nfunc (j *Job) Run(ctx context.Context, job *pb.Job) error {\n\ttime.Sleep(j.args.d)\n\treturn nil\n}\n"
  },
  {
    "path": "chapter/16/workflow/internal/service/jobs/register/tokenbucket/tokenbucket.go",
    "content": "/*\nPackage tokenbucket registers a job that is used to fetch a token from a token bucket.\n\nRegister name: \"tokenBucket\"\nArgs:\n\t\"bucket\"(mandatory): The name of the bucket\n\t\"fatal\"(mandatory): true if a failure should cause a fatal error, false if it should block until it gets one\nResult:\n\tIf the site is not in decom, will return a fatal error.\n*/\npackage tokenbucket\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/data/packages/sites\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/service/jobs\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/token\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/proto\"\n)\n\nvar buckets = map[string]*token.Bucket{}\n\n// This registers our Job on server startup.\nfunc init() {\n\tjobs.Register(\"tokenBucket\", newJob())\n\n\tsatDiskErase, err := token.New(1, 1, 30*time.Minute)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tbuckets[\"diskEraseSatellite\"] = satDiskErase\n}\n\ntype args struct {\n\tbucket string\n\tfatal  bool\n}\n\nfunc (a *args) validate(args map[string]string) error {\n\tmust := map[string]bool{\n\t\t\"bucket\": false,\n\t\t\"fatal\":  false,\n\t}\n\n\tfor k, v := range args {\n\t\tswitch k {\n\t\tcase \"bucket\":\n\t\t\tif _, ok := buckets[v]; !ok {\n\t\t\t\treturn fmt.Errorf(\"bucket(%s) was not a valid\", v)\n\t\t\t}\n\t\t\tmust[\"bucket\"] = true\n\t\t\ta.bucket = v\n\t\tcase \"fatal\":\n\t\t\tswitch v {\n\t\t\tcase \"true\":\n\t\t\t\ta.fatal = true\n\t\t\tcase \"false\":\n\t\t\t\ta.fatal = true\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"arg(fatal) was not true or false, was %q\", v)\n\t\t\t}\n\t\t\tmust[\"fatal\"] = true\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"invalid arg(%s)\", k)\n\t\t}\n\t}\n\n\tfor k, v := range must {\n\t\tif !v {\n\t\t\treturn fmt.Errorf(\"missing required arg(%s)\", k)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Job implements jobs.Job.\ntype Job struct {\n\tsites map[string]sites.Site\n\targs  args\n}\n\nfunc newJob() *Job {\n\treturn &Job{}\n}\n\n// Validate implements jobs.Job.Validate().\nfunc (j *Job) Validate(job *pb.Job) error {\n\ta := args{}\n\tif err := a.validate(job.Args); err != nil {\n\t\treturn err\n\t}\n\tj.args = a\n\treturn nil\n}\n\n// Run implements jobs.Job.Run().\nfunc (j *Job) Run(ctx context.Context, job *pb.Job) error {\n\tif j.args.fatal {\n\t\tvar cancel context.CancelFunc\n\t\tctx, cancel = context.WithTimeout(ctx, 1*time.Second)\n\t\tdefer cancel()\n\t}\n\tif err := buckets[j.args.bucket].Token(ctx); err != nil {\n\t\tif j.args.fatal {\n\t\t\treturn jobs.Fatalf(\"token(%s) not available\", j.args.bucket)\n\t\t}\n\t\treturn jobs.Fatalf(\"workflow cancelled before token(%s) was available\", j.args.bucket)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "chapter/16/workflow/internal/service/jobs/register/validatedecom/validatedecom.go",
    "content": "/*\nPackage validatedecom registers a job that is used to validate a site is set to state \"decom\".\n\nRegister name: \"validateDecom\"\nArgs:\n\t\"site\"(mandatory): The name of the site, like \"aaa\" or \"aba\"\n\t\"siteType\"(mandatory): The type of the site, like \"satellite\" or \"cluster\"\nResult:\n\tIf the site is not in decom, will return a fatal error.\n*/\npackage validatedecom\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/data/packages/sites\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/service/jobs\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/proto\"\n)\n\n// This registers our Job on server startup.\nfunc init() {\n\tjobs.Register(\"validateDecom\", newJob(sites.Data.Sites))\n}\n\ntype args struct {\n\tsite     string\n\tsiteType string\n}\n\nfunc (a *args) validate(args map[string]string) error {\n\tmust := map[string]bool{\n\t\t\"site\": false,\n\t\t\"type\": false,\n\t}\n\tvar siteData sites.Site\n\n\tfor k, v := range args {\n\t\tswitch k {\n\t\tcase \"site\":\n\t\t\ts, ok := sites.Data.Sites[v]\n\t\t\tif !ok {\n\t\t\t\treturn fmt.Errorf(\"site(%s) was not a valid site\", v)\n\t\t\t}\n\t\t\tmust[\"site\"] = true\n\t\t\ta.site = v\n\t\t\tsiteData = s\n\t\tcase \"type\":\n\t\t\tmust[\"type\"] = true\n\t\t\ta.siteType = v\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"invalid arg(%s)\", k)\n\t\t}\n\t}\n\n\tif siteData.Type != a.siteType {\n\t\treturn fmt.Errorf(\"site(%s) is type(%s), we expected(%s)\", a.site, siteData.Type, a.siteType)\n\t}\n\n\tif siteData.Status != \"decom\" {\n\t\treturn fmt.Errorf(\"site(%s) is not in the decom state, was in %q\", a.site, siteData.Status)\n\t}\n\n\tfor k, v := range must {\n\t\tif !v {\n\t\t\treturn fmt.Errorf(\"missing required arg(%s)\", k)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Job implements jobs.Job.\ntype Job struct {\n\tsites map[string]sites.Site\n\targs  args\n}\n\nfunc newJob(sites map[string]sites.Site) *Job {\n\treturn &Job{sites: sites}\n}\n\n// Validate implements jobs.Job.Validate().\nfunc (j *Job) Validate(job *pb.Job) error {\n\ta := args{}\n\tif err := a.validate(job.Args); err != nil {\n\t\treturn err\n\t}\n\tj.args = a\n\treturn nil\n}\n\n// Run implements jobs.Job.Run().\nfunc (j *Job) Run(ctx context.Context, job *pb.Job) error {\n\tsite, ok := sites.Data.Sites[j.args.site]\n\tif !ok {\n\t\treturn jobs.Fatalf(\"site(%s) is no longer in the sites file\", j.args.site)\n\t}\n\n\tif site.Status != \"decom\" {\n\t\treturn jobs.Fatalf(\"site(%s) was transitioned out of decom before Job ran\", j.args.site)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "chapter/16/workflow/internal/service/service.go",
    "content": "// Package service implements our gRPC service called Workflow.\npackage service\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/es\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/service/executor\"\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/proto\"\n)\n\n// active provides an entry for an actively executing workflow.\ntype active struct {\n\twork   *executor.Work\n\tstatus atomic.Value // *pb.StatusResp\n}\n\n// Workflow implements our gRPC service.\ntype Workflow struct {\n\t// storageDir is where we store workflow information.\n\tstorageDir string\n\n\t// mu protects active\n\tmu sync.Mutex\n\t// active tracks all active work that is occuring.\n\tactive map[string]*active\n\n\t// Required for gRPC to run, makes sure we have all the methods defined.\n\tpb.UnimplementedWorkflowServer\n}\n\n// New creates a new Workflow service.\nfunc New(storageDir string) (*Workflow, error) {\n\tstat, err := os.Stat(storageDir)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not stat the workflow storage(%s): %w\", storageDir, err)\n\t}\n\tif !stat.IsDir() {\n\t\treturn nil, fmt.Errorf(\"storageDir(%s) is not a directory\", storageDir)\n\t}\n\tu := \"ping_\" + uuid.NewString()\n\tp := filepath.Join(storageDir, u)\n\tf, err := os.OpenFile(p, os.O_CREATE+os.O_RDWR, 0600)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not open a file in storage(%s) for RDWR: %w\", storageDir, err)\n\t}\n\tf.Close()\n\tif err := os.Remove(p); err != nil {\n\t\treturn nil, fmt.Errorf(\"could not remove ping file(%s) in storage(%s)\", p, storageDir)\n\t}\n\treturn &Workflow{storageDir: storageDir, active: map[string]*active{}}, nil\n}\n\nvar submitRateLimit = make(chan struct{}, 10)\n\n// Submit submits a request to run a workflow.\nfunc (w *Workflow) Submit(ctx context.Context, req *pb.WorkReq) (*pb.WorkResp, error) {\n\tselect {\n\tcase submitRateLimit <- struct{}{}:\n\tdefault:\n\t\treturn nil, status.Errorf(codes.ResourceExhausted, \"too many requests\")\n\t}\n\tdefer func() { <-submitRateLimit }()\n\n\tesStatus := es.Data.Status(req.Name)\n\tif esStatus != es.Go {\n\t\treturn nil, status.Errorf(codes.Aborted, \"emergency stop for(%s) was %s\", req.Name, esStatus)\n\t}\n\n\tvalidateCtx, cancel := context.WithTimeout(ctx, 30*time.Second)\n\tdefer cancel()\n\n\tif err := executor.Validate(validateCtx, req); err != nil {\n\t\tif errors.Is(err, context.Canceled) {\n\t\t\treturn nil, status.Error(codes.DeadlineExceeded, err.Error())\n\t\t}\n\t\treturn nil, status.Error(codes.InvalidArgument, err.Error())\n\t}\n\tvar (\n\t\tid string\n\t\tp  string\n\t)\n\n\t// Loop until we get a unique ID that doesn't exist on the filesystem.\n\tfor {\n\t\tu, err := uuid.NewUUID()\n\t\tif err != nil {\n\t\t\treturn nil, status.Errorf(codes.Internal, \"problem getting UUIDv1; %s\", err.Error())\n\t\t}\n\t\tid = u.String()\n\t\tp = filepath.Join(w.storageDir, id)\n\n\t\t_, err = os.Stat(p) // Make sure this doesn't alreay exist.\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tresp := &pb.WorkResp{Id: id}\n\n\tb, err := proto.Marshal(req)\n\tif err != nil {\n\t\treturn nil, status.Errorf(codes.InvalidArgument, \"could not marshal the request: %s\", err)\n\t}\n\n\tf, err := os.OpenFile(p, os.O_CREATE+os.O_WRONLY, 0600)\n\tif err != nil {\n\t\treturn nil, status.Errorf(codes.Internal, \"could not open file in storageDir(%s): %s\", w.storageDir, err)\n\t}\n\tdefer f.Close()\n\n\tif _, err := f.Write(b); err != nil {\n\t\treturn nil, status.Errorf(codes.Internal, \"problem writing request to storage: %s\", err)\n\t}\n\n\treturn resp, nil\n}\n\nvar executeRateLimit = make(chan struct{}, 10)\n\n// Exec requests that the system execute a submitted workflow.\nfunc (w *Workflow) Exec(ctx context.Context, req *pb.ExecReq) (*pb.ExecResp, error) {\n\tselect {\n\tcase executeRateLimit <- struct{}{}:\n\tdefault:\n\t\treturn nil, status.Errorf(codes.ResourceExhausted, \"too many requests\")\n\t}\n\tdefer func() { <-executeRateLimit }()\n\n\tp := filepath.Join(w.storageDir, req.Id)\n\tstatP := filepath.Join(w.storageDir, req.Id+\"_status\")\n\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\n\t_, ok := w.active[req.Id]\n\tif ok {\n\t\treturn nil, status.Errorf(codes.AlreadyExists, \"Workflow(%s) is already running\", req.Id)\n\t}\n\n\t_, err := os.Stat(statP)\n\tif err == nil {\n\t\treturn nil, status.Errorf(codes.AlreadyExists, \"Workflow(%s) already executing or executed\", req.Id)\n\t}\n\n\tu, err := uuid.Parse(req.Id)\n\tif err != nil {\n\t\treturn nil, status.Errorf(codes.InvalidArgument, \"Id(%s) is not a valid value: %s\", req.Id, err)\n\t}\n\n\tt := time.Unix(u.Time().UnixTime())\n\tif time.Now().Sub(t) > 1*time.Hour {\n\t\treturn nil, status.Errorf(codes.FailedPrecondition, \"Id(%s) is older than 1 hour and cannot be started\", req.Id)\n\t}\n\n\tb, err := os.ReadFile(p)\n\tif err != nil {\n\t\treturn nil, status.Errorf(codes.NotFound, \"Workflow(%s) not found\", req.Id)\n\t}\n\n\tworkReq := &pb.WorkReq{}\n\tif err := proto.Unmarshal(b, workReq); err != nil {\n\t\treturn nil, status.Errorf(codes.Internal, \"Workflow(%s) could not be unmarshalled: %s\", req.Id, err)\n\t}\n\n\tesStatus := es.Data.Status(workReq.Name)\n\tif esStatus != es.Go {\n\t\treturn nil, status.Errorf(codes.Aborted, \"emergency stop for(%s) was %s\", workReq.Name, esStatus)\n\t}\n\n\t// Write our status file to indicate we have started working on this.\n\tstatusResp := statusFromWork(workReq)\n\tstatusB, err := proto.Marshal(statusResp)\n\tif err != nil {\n\t\treturn nil, status.Errorf(codes.InvalidArgument, \"could not marshal the work's status proto: %s\", err)\n\t}\n\n\tif err := os.WriteFile(statP, statusB, 0600); err != nil {\n\t\treturn nil, status.Errorf(codes.Internal, \"problem writing status to storage: %s\", err)\n\t}\n\n\twork := executor.New(workReq, statusResp)\n\tactive := &active{work: work}\n\tactive.status.Store(proto.Clone(statusResp).(*pb.StatusResp))\n\tw.active[req.Id] = active\n\n\t// Run our work and get the first state change.\n\tch := work.Run(context.Background())\n\tactive.status.Store(<-ch)\n\twriteIn := statusWriter(statP)\n\n\t// Update our status as it changes in memory and on disk.\n\t// Cleanup our list of active work when we are done.\n\tgo func() {\n\t\tfor status := range ch {\n\t\t\t// Record our status in memory\n\t\t\tactive.status.Store(status)\n\n\t\t\t// Record our status on disk. If there is an entry pending,\n\t\t\t// remove it for the latest entry.\n\t\t\tselect {\n\t\t\tcase writeIn <- status:\n\t\t\tdefault:\n\t\t\t\tselect {\n\t\t\t\tcase <-writeIn:\n\t\t\t\tdefault:\n\t\t\t\t}\n\t\t\t\twriteIn <- status\n\t\t\t}\n\t\t}\n\t\tw.mu.Lock()\n\t\tdelete(w.active, req.Id)\n\t\tw.mu.Unlock()\n\t}()\n\n\treturn &pb.ExecResp{}, nil\n}\n\nvar statusRateLimit = make(chan struct{}, 10)\n\n// Status is used to query for the status of a workflow.\nfunc (w *Workflow) Status(ctx context.Context, req *pb.StatusReq) (*pb.StatusResp, error) {\n\tselect {\n\tcase statusRateLimit <- struct{}{}:\n\tdefault:\n\t\treturn nil, status.Errorf(codes.ResourceExhausted, \"too many requests\")\n\t}\n\tdefer func() { <-statusRateLimit }()\n\n\tw.mu.Lock()\n\ta := w.active[req.Id]\n\tw.mu.Unlock()\n\tif a != nil {\n\t\treturn a.status.Load().(*pb.StatusResp), nil\n\t}\n\t// This ID is not currently running, so look in storage.\n\tp := filepath.Join(w.storageDir, req.Id+\"_status\")\n\tb, err := os.ReadFile(p)\n\tif err != nil {\n\t\treturn nil, status.Errorf(codes.InvalidArgument, \"work ID(%s) was not found\", req.Id)\n\t}\n\tresp := &pb.StatusResp{}\n\tif err := proto.Unmarshal(b, resp); err != nil {\n\t\treturn nil, status.Errorf(codes.Internal, \"work ID(%s) data was corrupted on disk\", req.Id)\n\t}\n\treturn resp, nil\n}\n\n// statusFromWork takes a WorkReq and generates the corresponding StatusResp.\nfunc statusFromWork(req *pb.WorkReq) *pb.StatusResp {\n\tresp := &pb.StatusResp{Name: req.Name, Desc: req.Desc, Status: pb.Status_StatusNotStarted}\n\n\tfor _, b := range req.Blocks {\n\t\tsb := &pb.BlockStatus{\n\t\t\tDesc:   b.Desc,\n\t\t\tStatus: pb.Status_StatusNotStarted,\n\t\t}\n\t\tfor _, j := range b.Jobs {\n\t\t\tsj := &pb.JobStatus{\n\t\t\t\tName:   j.Name,\n\t\t\t\tDesc:   j.Desc,\n\t\t\t\tArgs:   j.Args,\n\t\t\t\tStatus: pb.Status_StatusNotStarted,\n\t\t\t}\n\t\t\tsb.Jobs = append(sb.Jobs, sj)\n\t\t}\n\t\tresp.Blocks = append(resp.Blocks, sb)\n\t}\n\treturn resp\n}\n\nfunc statusWriter(p string) (in chan *pb.StatusResp) {\n\tin = make(chan *pb.StatusResp, 1)\n\n\tgo func() {\n\t\tfor status := range in {\n\t\t\tb, err := proto.Marshal(status)\n\t\t\tif err != nil {\n\t\t\t\tlog.Println(\"could not marshal a status proto: \", err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif err := os.WriteFile(p, b, 0600); err != nil {\n\t\t\t\tlog.Println(\"cannot write a status update to disk, this is bad: \", err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t}()\n\treturn in\n}\n"
  },
  {
    "path": "chapter/16/workflow/internal/token/token.go",
    "content": "// Package token contains a standard token bucket implementation.\npackage token\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// Bucket is an implementation of a standard token Bucket. The Bucket is refilled at some interval\n// to a maximum value.\ntype Bucket struct {\n\t// tokens represents an full token Bucket at some size. Every entry into the Bucket\n\t// remove capacity.\n\ttokens chan struct{}\n\t// ticker is a ticket that we use to refresh our tokens at some interval.\n\tticker *time.Ticker\n}\n\n// New creates a Bucket instance. size is how many tokens we can hold. incr is the amount of tokens\n// to add at a time. interval is how often to add tokens.\nfunc New(size, incr int, interval time.Duration) (*Bucket, error) {\n\tif size < 1 {\n\t\treturn nil, fmt.Errorf(\"size must be > 1\")\n\t}\n\tif interval < 1*time.Second {\n\t\treturn nil, fmt.Errorf(\"interval must be < 1 second\")\n\t}\n\tif incr < 1 {\n\t\treturn nil, fmt.Errorf(\"incr must be > 0\")\n\t}\n\n\tb := Bucket{tokens: make(chan struct{}, size), ticker: time.NewTicker(interval)}\n\t// This goroutine adds tokens by removing items from our channel. This seems like the\n\t// opposite logic of what you'd expect, but this is actually an efficient way of implementing\n\t// a token Bucket usign channels.\n\tgo func() {\n\t\tfor _ = range b.ticker.C {\n\t\t\tfor i := 0; i < incr; i++ {\n\t\t\t\tselect {\n\t\t\t\tcase <-b.tokens:\n\t\t\t\t\tcontinue\n\t\t\t\tdefault:\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}()\n\treturn &b, nil\n}\n\n// close stops the token Bucket's goroutine. This should be called before throwing away the Bucket.\n// If you use the token Bucket after this is called, this can cause major problems like causing the\n// token() call to block forever, as there are no more tokens being added.\nfunc (b *Bucket) Close() {\n\tb.ticker.Stop()\n\tclose(b.tokens)\n}\n\n// token blocks until a token is available or the context is cancelled. An error is only returned\n// if the context is cancelled.\nfunc (b *Bucket) Token(ctx context.Context) error {\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn ctx.Err()\n\tcase b.tokens <- struct{}{}:\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "chapter/16/workflow/proto/buf.gen.yaml",
    "content": "version: v1\nplugins:\n  - name: go\n    out: ./\n    opt:\n      - paths=source_relative\n  - name: go-grpc\n    out: ./\n    opt:\n      - paths=source_relative\n"
  },
  {
    "path": "chapter/16/workflow/proto/buf.yaml",
    "content": "version: v1\nlint:\n  use:\n    - DEFAULT\nbreaking:\n  use:\n    - FILE\n"
  },
  {
    "path": "chapter/16/workflow/proto/custom.go",
    "content": "package diskerase\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/fatih/color\"\n\t\"github.com/rodaine/table\"\n)\n\n// CLISummary() provides the StatusResp in a summary format that is useful for\n// viewing in a CLI application. It summarizes all blocks into single lines except\n// for the block that is currently running.\nfunc (x *StatusResp) CLISummary(id string) string {\n\tif len(x.Blocks) == 0 {\n\t\treturn \"no blocks defined\"\n\t}\n\n\tblockTitle := color.New(color.FgCyan).Add(color.Underline)\n\tname := color.New(color.FgGreen)\n\tdesc := color.New(color.FgYellow)\n\n\tbuff := strings.Builder{}\n\tbuff.WriteString(fmt.Sprintf(\"Time: %s\\n\", time.Now().Format(time.RFC1123)))\n\tbuff.WriteString(fmt.Sprintf(\"Workflow: %s\\n\", id))\n\tname.Fprintln(&buff, \"Name: \"+x.Name)\n\tdesc.Fprintln(&buff, \"Description: \"+x.Desc)\n\n\tif i, block := x.findRunning(x.Blocks); i != -1 {\n\t\tblockTitle.Fprintln(&buff, fmt.Sprintf(\"\\nRunning Block(%d): %s\", i, block.Desc))\n\t\tx.writeRunning(&buff, block)\n\t}\n\n\tblockTitle.Fprintln(&buff, \"\\nBlock Summaries\")\n\tx.writeOthers(&buff, x.Blocks)\n\n\treturn buff.String()\n}\n\nfunc (x *StatusResp) writeOthers(buff *strings.Builder, blocks []*BlockStatus) {\n\theaderFmt := color.New(color.FgGreen, color.Underline).SprintfFunc()\n\tcolumnFmt := color.New(color.FgYellow).SprintfFunc()\n\n\ttbl := table.New(\"Block Number\", \"Desc\", \"Status\").WithWriter(buff)\n\ttbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt)\n\n\tfor i, block := range blocks {\n\t\ttbl.AddRow(i, block.Desc, block.Status)\n\t\tif block.Status == Status_StatusRunning {\n\t\t\tcontinue\n\t\t}\n\t}\n\ttbl.Print()\n}\n\nfunc (x *StatusResp) findRunning(blocks []*BlockStatus) (int, *BlockStatus) {\n\tfor i, b := range blocks {\n\t\tif b.Status == Status_StatusRunning {\n\t\t\treturn i, b\n\t\t}\n\t}\n\treturn -1, nil\n}\n\nfunc (x *StatusResp) writeRunning(buff *strings.Builder, block *BlockStatus) {\n\theaderFmt := color.New(color.FgGreen, color.Underline).SprintfFunc()\n\tcolumnFmt := color.New(color.FgYellow).SprintfFunc()\n\n\ttbl := table.New(\"Job Number\", \"Desc\", \"Status\").WithWriter(buff)\n\ttbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt)\n\n\tfor i, job := range block.Jobs {\n\t\ttbl.AddRow(i, job.Desc, job.Status)\n\t}\n\ttbl.Print()\n\treturn\n}\n"
  },
  {
    "path": "chapter/16/workflow/proto/diskerase.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.25.0-devel\n// \tprotoc        v3.18.0\n// source: diskerase.proto\n\npackage diskerase\n\nimport (\n\treflect \"reflect\"\n\tsync \"sync\"\n\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\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\n// Status details the status of a Block or Job.\ntype Status int32\n\nconst (\n\t// Indicates that there is some bug\n\t// as the Status for the object was not set.\n\tStatus_StatusUnknown Status = 0\n\t// The WorkReq, Block or Job has not started execution.\n\tStatus_StatusNotStarted Status = 1\n\t// The WorkReq, Block or Job is currently executing.\n\tStatus_StatusRunning Status = 2\n\t// The WorkReq, Block or Job has failed.\n\tStatus_StatusFailed Status = 3\n\t// The WorkReq, Block or Job has completed.\n\tStatus_StatusCompleted Status = 4\n)\n\n// Enum value maps for Status.\nvar (\n\tStatus_name = map[int32]string{\n\t\t0: \"StatusUnknown\",\n\t\t1: \"StatusNotStarted\",\n\t\t2: \"StatusRunning\",\n\t\t3: \"StatusFailed\",\n\t\t4: \"StatusCompleted\",\n\t}\n\tStatus_value = map[string]int32{\n\t\t\"StatusUnknown\":    0,\n\t\t\"StatusNotStarted\": 1,\n\t\t\"StatusRunning\":    2,\n\t\t\"StatusFailed\":     3,\n\t\t\"StatusCompleted\":  4,\n\t}\n)\n\nfunc (x Status) Enum() *Status {\n\tp := new(Status)\n\t*p = x\n\treturn p\n}\n\nfunc (x Status) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (Status) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_diskerase_proto_enumTypes[0].Descriptor()\n}\n\nfunc (Status) Type() protoreflect.EnumType {\n\treturn &file_diskerase_proto_enumTypes[0]\n}\n\nfunc (x Status) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use Status.Descriptor instead.\nfunc (Status) EnumDescriptor() ([]byte, []int) {\n\treturn file_diskerase_proto_rawDescGZIP(), []int{0}\n}\n\n// WorkReq is the definition of some work to be done by the system.\ntype WorkReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// This is used to describe the work to be done. This name\n\t// must be authorized by having a policy with the same name\n\t// in the server's policies.json fie.\n\tName string `protobuf:\"bytes,1,opt,name=name,proto3\" json:\"name,omitempty\"`\n\t// A description of what this is doing.\n\tDesc string `protobuf:\"bytes,2,opt,name=desc,proto3\" json:\"desc,omitempty\"`\n\t// These are groupings of Jobs. Each block is executed one at\n\t// a time.\n\tBlocks []*Block `protobuf:\"bytes,3,rep,name=blocks,proto3\" json:\"blocks,omitempty\"`\n}\n\nfunc (x *WorkReq) Reset() {\n\t*x = WorkReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_diskerase_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *WorkReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*WorkReq) ProtoMessage() {}\n\nfunc (x *WorkReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_diskerase_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 WorkReq.ProtoReflect.Descriptor instead.\nfunc (*WorkReq) Descriptor() ([]byte, []int) {\n\treturn file_diskerase_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *WorkReq) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\nfunc (x *WorkReq) GetDesc() string {\n\tif x != nil {\n\t\treturn x.Desc\n\t}\n\treturn \"\"\n}\n\nfunc (x *WorkReq) GetBlocks() []*Block {\n\tif x != nil {\n\t\treturn x.Blocks\n\t}\n\treturn nil\n}\n\n// WorkResp details the ID that will be used to refer to a submitted WorkReq.\ntype WorkResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// This is the unique ID for this WorkReq.\n\tId string `protobuf:\"bytes,1,opt,name=id,proto3\" json:\"id,omitempty\"`\n}\n\nfunc (x *WorkResp) Reset() {\n\t*x = WorkResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_diskerase_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *WorkResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*WorkResp) ProtoMessage() {}\n\nfunc (x *WorkResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_diskerase_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 WorkResp.ProtoReflect.Descriptor instead.\nfunc (*WorkResp) Descriptor() ([]byte, []int) {\n\treturn file_diskerase_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *WorkResp) GetId() string {\n\tif x != nil {\n\t\treturn x.Id\n\t}\n\treturn \"\"\n}\n\n// Block is a grouping of Jobs that will be executed concurrently\n// at some rate.\ntype Block struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// This describes what the Block is doing.\n\tDesc string `protobuf:\"bytes,1,opt,name=desc,proto3\" json:\"desc,omitempty\"`\n\t// The amount of concurrency executions. < 1 will default to 1.\n\tRateLimit int32 `protobuf:\"varint,2,opt,name=rate_limit,json=rateLimit,proto3\" json:\"rate_limit,omitempty\"`\n\t// The Jobs to to execute in this Block.\n\tJobs []*Job `protobuf:\"bytes,3,rep,name=jobs,proto3\" json:\"jobs,omitempty\"`\n}\n\nfunc (x *Block) Reset() {\n\t*x = Block{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_diskerase_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Block) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Block) ProtoMessage() {}\n\nfunc (x *Block) ProtoReflect() protoreflect.Message {\n\tmi := &file_diskerase_proto_msgTypes[2]\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 Block.ProtoReflect.Descriptor instead.\nfunc (*Block) Descriptor() ([]byte, []int) {\n\treturn file_diskerase_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *Block) GetDesc() string {\n\tif x != nil {\n\t\treturn x.Desc\n\t}\n\treturn \"\"\n}\n\nfunc (x *Block) GetRateLimit() int32 {\n\tif x != nil {\n\t\treturn x.RateLimit\n\t}\n\treturn 0\n}\n\nfunc (x *Block) GetJobs() []*Job {\n\tif x != nil {\n\t\treturn x.Jobs\n\t}\n\treturn nil\n}\n\n// Job refers to a Job action that is defined on the server.\ntype Job struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// This is the name of the Job, which must be registered on\n\t// the server.\n\tName string `protobuf:\"bytes,1,opt,name=name,proto3\" json:\"name,omitempty\"`\n\t// This is a description of what the job is doing.\n\tDesc string `protobuf:\"bytes,2,opt,name=desc,proto3\" json:\"desc,omitempty\"`\n\t// A mapping of key/value arguments. While the value is a string,\n\t// it can represent non-string data and will be converted by the\n\t// Job on the server. See the Job definition for a list of arguments\n\t// that are mandatory and optional.\n\tArgs map[string]string `protobuf:\"bytes,3,rep,name=args,proto3\" json:\"args,omitempty\" protobuf_key:\"bytes,1,opt,name=key,proto3\" protobuf_val:\"bytes,2,opt,name=value,proto3\"`\n}\n\nfunc (x *Job) Reset() {\n\t*x = Job{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_diskerase_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Job) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Job) ProtoMessage() {}\n\nfunc (x *Job) ProtoReflect() protoreflect.Message {\n\tmi := &file_diskerase_proto_msgTypes[3]\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 Job.ProtoReflect.Descriptor instead.\nfunc (*Job) Descriptor() ([]byte, []int) {\n\treturn file_diskerase_proto_rawDescGZIP(), []int{3}\n}\n\nfunc (x *Job) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\nfunc (x *Job) GetDesc() string {\n\tif x != nil {\n\t\treturn x.Desc\n\t}\n\treturn \"\"\n}\n\nfunc (x *Job) GetArgs() map[string]string {\n\tif x != nil {\n\t\treturn x.Args\n\t}\n\treturn nil\n}\n\n// ExecReq is used to tell the server to execute a WorkReq\n// that was previously submitted.\ntype ExecReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// This is the unique ID of the WorkReq given back\n\t// by WorkResp.\n\tId string `protobuf:\"bytes,1,opt,name=id,proto3\" json:\"id,omitempty\"`\n}\n\nfunc (x *ExecReq) Reset() {\n\t*x = ExecReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_diskerase_proto_msgTypes[4]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ExecReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ExecReq) ProtoMessage() {}\n\nfunc (x *ExecReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_diskerase_proto_msgTypes[4]\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 ExecReq.ProtoReflect.Descriptor instead.\nfunc (*ExecReq) Descriptor() ([]byte, []int) {\n\treturn file_diskerase_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (x *ExecReq) GetId() string {\n\tif x != nil {\n\t\treturn x.Id\n\t}\n\treturn \"\"\n}\n\n// ExecResp is the response from an ExecReq.\ntype ExecResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *ExecResp) Reset() {\n\t*x = ExecResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_diskerase_proto_msgTypes[5]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ExecResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ExecResp) ProtoMessage() {}\n\nfunc (x *ExecResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_diskerase_proto_msgTypes[5]\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 ExecResp.ProtoReflect.Descriptor instead.\nfunc (*ExecResp) Descriptor() ([]byte, []int) {\n\treturn file_diskerase_proto_rawDescGZIP(), []int{5}\n}\n\n// StatusReq requests a status update from the server.\ntype StatusReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The unique ID of the WorkReq.\n\tId string `protobuf:\"bytes,1,opt,name=id,proto3\" json:\"id,omitempty\"`\n}\n\nfunc (x *StatusReq) Reset() {\n\t*x = StatusReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_diskerase_proto_msgTypes[6]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *StatusReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*StatusReq) ProtoMessage() {}\n\nfunc (x *StatusReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_diskerase_proto_msgTypes[6]\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 StatusReq.ProtoReflect.Descriptor instead.\nfunc (*StatusReq) Descriptor() ([]byte, []int) {\n\treturn file_diskerase_proto_rawDescGZIP(), []int{6}\n}\n\nfunc (x *StatusReq) GetId() string {\n\tif x != nil {\n\t\treturn x.Id\n\t}\n\treturn \"\"\n}\n\n// StatusResp is the status of WorkReq.\ntype StatusResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The name of the WorkReq.\n\tName string `protobuf:\"bytes,1,opt,name=name,proto3\" json:\"name,omitempty\"`\n\t// The description of the WorkReq.\n\tDesc string `protobuf:\"bytes,2,opt,name=desc,proto3\" json:\"desc,omitempty\"`\n\t// The overall status of the WorkReq.\n\tStatus Status `protobuf:\"varint,3,opt,name=status,proto3,enum=diskerase.Status\" json:\"status,omitempty\"`\n\t// The status information on the Blocks.\n\tBlocks []*BlockStatus `protobuf:\"bytes,4,rep,name=blocks,proto3\" json:\"blocks,omitempty\"`\n\t// If we are SatusFailed or StatusCompleted, if\n\t// there were any errors when run.\n\tHadErrors bool `protobuf:\"varint,5,opt,name=had_errors,json=hadErrors,proto3\" json:\"had_errors,omitempty\"`\n\t// If the WorkReq was stopped with emergency stop.\n\tWasEsStopped bool `protobuf:\"varint,6,opt,name=was_es_stopped,json=wasEsStopped,proto3\" json:\"was_es_stopped,omitempty\"`\n}\n\nfunc (x *StatusResp) Reset() {\n\t*x = StatusResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_diskerase_proto_msgTypes[7]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *StatusResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*StatusResp) ProtoMessage() {}\n\nfunc (x *StatusResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_diskerase_proto_msgTypes[7]\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 StatusResp.ProtoReflect.Descriptor instead.\nfunc (*StatusResp) Descriptor() ([]byte, []int) {\n\treturn file_diskerase_proto_rawDescGZIP(), []int{7}\n}\n\nfunc (x *StatusResp) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\nfunc (x *StatusResp) GetDesc() string {\n\tif x != nil {\n\t\treturn x.Desc\n\t}\n\treturn \"\"\n}\n\nfunc (x *StatusResp) GetStatus() Status {\n\tif x != nil {\n\t\treturn x.Status\n\t}\n\treturn Status_StatusUnknown\n}\n\nfunc (x *StatusResp) GetBlocks() []*BlockStatus {\n\tif x != nil {\n\t\treturn x.Blocks\n\t}\n\treturn nil\n}\n\nfunc (x *StatusResp) GetHadErrors() bool {\n\tif x != nil {\n\t\treturn x.HadErrors\n\t}\n\treturn false\n}\n\nfunc (x *StatusResp) GetWasEsStopped() bool {\n\tif x != nil {\n\t\treturn x.WasEsStopped\n\t}\n\treturn false\n}\n\n// BlockStatus holds the status of block execution.\ntype BlockStatus struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The description of the block.\n\tDesc string `protobuf:\"bytes,1,opt,name=desc,proto3\" json:\"desc,omitempty\"`\n\t// The status of the block.\n\tStatus Status `protobuf:\"varint,2,opt,name=status,proto3,enum=diskerase.Status\" json:\"status,omitempty\"`\n\t// If there any errors in Jobs in the Block.\n\tHasError bool `protobuf:\"varint,3,opt,name=has_error,json=hasError,proto3\" json:\"has_error,omitempty\"`\n\t// The status of Jobs in the Block.\n\tJobs []*JobStatus `protobuf:\"bytes,4,rep,name=jobs,proto3\" json:\"jobs,omitempty\"`\n}\n\nfunc (x *BlockStatus) Reset() {\n\t*x = BlockStatus{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_diskerase_proto_msgTypes[8]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *BlockStatus) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*BlockStatus) ProtoMessage() {}\n\nfunc (x *BlockStatus) ProtoReflect() protoreflect.Message {\n\tmi := &file_diskerase_proto_msgTypes[8]\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 BlockStatus.ProtoReflect.Descriptor instead.\nfunc (*BlockStatus) Descriptor() ([]byte, []int) {\n\treturn file_diskerase_proto_rawDescGZIP(), []int{8}\n}\n\nfunc (x *BlockStatus) GetDesc() string {\n\tif x != nil {\n\t\treturn x.Desc\n\t}\n\treturn \"\"\n}\n\nfunc (x *BlockStatus) GetStatus() Status {\n\tif x != nil {\n\t\treturn x.Status\n\t}\n\treturn Status_StatusUnknown\n}\n\nfunc (x *BlockStatus) GetHasError() bool {\n\tif x != nil {\n\t\treturn x.HasError\n\t}\n\treturn false\n}\n\nfunc (x *BlockStatus) GetJobs() []*JobStatus {\n\tif x != nil {\n\t\treturn x.Jobs\n\t}\n\treturn nil\n}\n\n// JobStatus holds the status of the Jobs.\ntype JobStatus struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The name of the Job called.\n\tName string `protobuf:\"bytes,1,opt,name=name,proto3\" json:\"name,omitempty\"`\n\t// The description of the Job.\n\tDesc string `protobuf:\"bytes,2,opt,name=desc,proto3\" json:\"desc,omitempty\"`\n\t// The args for the Job.\n\tArgs map[string]string `protobuf:\"bytes,3,rep,name=args,proto3\" json:\"args,omitempty\" protobuf_key:\"bytes,1,opt,name=key,proto3\" protobuf_val:\"bytes,2,opt,name=value,proto3\"`\n\t// The status of the Job.\n\tStatus Status `protobuf:\"varint,4,opt,name=status,proto3,enum=diskerase.Status\" json:\"status,omitempty\"`\n\t// The error, if there was one.\n\tError string `protobuf:\"bytes,5,opt,name=error,proto3\" json:\"error,omitempty\"`\n}\n\nfunc (x *JobStatus) Reset() {\n\t*x = JobStatus{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_diskerase_proto_msgTypes[9]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *JobStatus) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*JobStatus) ProtoMessage() {}\n\nfunc (x *JobStatus) ProtoReflect() protoreflect.Message {\n\tmi := &file_diskerase_proto_msgTypes[9]\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 JobStatus.ProtoReflect.Descriptor instead.\nfunc (*JobStatus) Descriptor() ([]byte, []int) {\n\treturn file_diskerase_proto_rawDescGZIP(), []int{9}\n}\n\nfunc (x *JobStatus) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\nfunc (x *JobStatus) GetDesc() string {\n\tif x != nil {\n\t\treturn x.Desc\n\t}\n\treturn \"\"\n}\n\nfunc (x *JobStatus) GetArgs() map[string]string {\n\tif x != nil {\n\t\treturn x.Args\n\t}\n\treturn nil\n}\n\nfunc (x *JobStatus) GetStatus() Status {\n\tif x != nil {\n\t\treturn x.Status\n\t}\n\treturn Status_StatusUnknown\n}\n\nfunc (x *JobStatus) GetError() string {\n\tif x != nil {\n\t\treturn x.Error\n\t}\n\treturn \"\"\n}\n\nvar File_diskerase_proto protoreflect.FileDescriptor\n\nvar file_diskerase_proto_rawDesc = []byte{\n\t0x0a, 0x0f, 0x64, 0x69, 0x73, 0x6b, 0x65, 0x72, 0x61, 0x73, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x12, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x65, 0x72, 0x61, 0x73, 0x65, 0x22, 0x5b, 0x0a, 0x07,\n\t0x57, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,\n\t0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64,\n\t0x65, 0x73, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x63, 0x12,\n\t0x28, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32,\n\t0x10, 0x2e, 0x64, 0x69, 0x73, 0x6b, 0x65, 0x72, 0x61, 0x73, 0x65, 0x2e, 0x42, 0x6c, 0x6f, 0x63,\n\t0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x22, 0x1a, 0x0a, 0x08, 0x57, 0x6f, 0x72,\n\t0x6b, 0x52, 0x65, 0x73, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x5e, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x12,\n\t0x0a, 0x04, 0x64, 0x65, 0x73, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65,\n\t0x73, 0x63, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74,\n\t0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x72, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69,\n\t0x74, 0x12, 0x22, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32,\n\t0x0e, 0x2e, 0x64, 0x69, 0x73, 0x6b, 0x65, 0x72, 0x61, 0x73, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x52,\n\t0x04, 0x6a, 0x6f, 0x62, 0x73, 0x22, 0x94, 0x01, 0x0a, 0x03, 0x4a, 0x6f, 0x62, 0x12, 0x12, 0x0a,\n\t0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,\n\t0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,\n\t0x04, 0x64, 0x65, 0x73, 0x63, 0x12, 0x2c, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x03, 0x20,\n\t0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x64, 0x69, 0x73, 0x6b, 0x65, 0x72, 0x61, 0x73, 0x65, 0x2e,\n\t0x4a, 0x6f, 0x62, 0x2e, 0x41, 0x72, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x61,\n\t0x72, 0x67, 0x73, 0x1a, 0x37, 0x0a, 0x09, 0x41, 0x72, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,\n\t0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,\n\t0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,\n\t0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x19, 0x0a, 0x07,\n\t0x45, 0x78, 0x65, 0x63, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x0a, 0x0a, 0x08, 0x45, 0x78, 0x65, 0x63, 0x52,\n\t0x65, 0x73, 0x70, 0x22, 0x1b, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71,\n\t0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64,\n\t0x22, 0xd4, 0x01, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12,\n\t0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,\n\t0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28,\n\t0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x63, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,\n\t0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x64, 0x69, 0x73, 0x6b, 0x65, 0x72,\n\t0x61, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74,\n\t0x75, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x03,\n\t0x28, 0x0b, 0x32, 0x16, 0x2e, 0x64, 0x69, 0x73, 0x6b, 0x65, 0x72, 0x61, 0x73, 0x65, 0x2e, 0x42,\n\t0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63,\n\t0x6b, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x61, 0x64, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73,\n\t0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x68, 0x61, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72,\n\t0x73, 0x12, 0x24, 0x0a, 0x0e, 0x77, 0x61, 0x73, 0x5f, 0x65, 0x73, 0x5f, 0x73, 0x74, 0x6f, 0x70,\n\t0x70, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x77, 0x61, 0x73, 0x45, 0x73,\n\t0x53, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x22, 0x93, 0x01, 0x0a, 0x0b, 0x42, 0x6c, 0x6f, 0x63,\n\t0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x63, 0x18,\n\t0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x63, 0x12, 0x29, 0x0a, 0x06, 0x73,\n\t0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x64, 0x69,\n\t0x73, 0x6b, 0x65, 0x72, 0x61, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06,\n\t0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x61, 0x73, 0x5f, 0x65, 0x72,\n\t0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x68, 0x61, 0x73, 0x45, 0x72,\n\t0x72, 0x6f, 0x72, 0x12, 0x28, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28,\n\t0x0b, 0x32, 0x14, 0x2e, 0x64, 0x69, 0x73, 0x6b, 0x65, 0x72, 0x61, 0x73, 0x65, 0x2e, 0x4a, 0x6f,\n\t0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x22, 0xe1, 0x01,\n\t0x0a, 0x09, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e,\n\t0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,\n\t0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64,\n\t0x65, 0x73, 0x63, 0x12, 0x32, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,\n\t0x0b, 0x32, 0x1e, 0x2e, 0x64, 0x69, 0x73, 0x6b, 0x65, 0x72, 0x61, 0x73, 0x65, 0x2e, 0x4a, 0x6f,\n\t0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x41, 0x72, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72,\n\t0x79, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,\n\t0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x64, 0x69, 0x73, 0x6b, 0x65, 0x72,\n\t0x61, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74,\n\t0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28,\n\t0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x1a, 0x37, 0x0a, 0x09, 0x41, 0x72, 0x67, 0x73,\n\t0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,\n\t0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,\n\t0x01, 0x2a, 0x6b, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x11, 0x0a, 0x0d, 0x53,\n\t0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x14,\n\t0x0a, 0x10, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4e, 0x6f, 0x74, 0x53, 0x74, 0x61, 0x72, 0x74,\n\t0x65, 0x64, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x75,\n\t0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x75,\n\t0x73, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x74, 0x61,\n\t0x74, 0x75, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x10, 0x04, 0x32, 0xab,\n\t0x01, 0x0a, 0x08, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x33, 0x0a, 0x06, 0x53,\n\t0x75, 0x62, 0x6d, 0x69, 0x74, 0x12, 0x12, 0x2e, 0x64, 0x69, 0x73, 0x6b, 0x65, 0x72, 0x61, 0x73,\n\t0x65, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x64, 0x69, 0x73, 0x6b,\n\t0x65, 0x72, 0x61, 0x73, 0x65, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00,\n\t0x12, 0x31, 0x0a, 0x04, 0x45, 0x78, 0x65, 0x63, 0x12, 0x12, 0x2e, 0x64, 0x69, 0x73, 0x6b, 0x65,\n\t0x72, 0x61, 0x73, 0x65, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x64,\n\t0x69, 0x73, 0x6b, 0x65, 0x72, 0x61, 0x73, 0x65, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x52, 0x65, 0x73,\n\t0x70, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x2e,\n\t0x64, 0x69, 0x73, 0x6b, 0x65, 0x72, 0x61, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,\n\t0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x64, 0x69, 0x73, 0x6b, 0x65, 0x72, 0x61, 0x73, 0x65, 0x2e,\n\t0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x42, 0x4f, 0x5a, 0x4d,\n\t0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x61, 0x63, 0x6b, 0x74,\n\t0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x2f, 0x47, 0x6f, 0x2d, 0x66, 0x6f,\n\t0x72, 0x2d, 0x44, 0x65, 0x76, 0x4f, 0x70, 0x73, 0x2f, 0x63, 0x68, 0x61, 0x70, 0x74, 0x65, 0x72,\n\t0x2f, 0x31, 0x38, 0x2f, 0x64, 0x69, 0x73, 0x6b, 0x65, 0x72, 0x61, 0x73, 0x65, 0x2f, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x69, 0x73, 0x6b, 0x65, 0x72, 0x61, 0x73, 0x65, 0x62, 0x06, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_diskerase_proto_rawDescOnce sync.Once\n\tfile_diskerase_proto_rawDescData = file_diskerase_proto_rawDesc\n)\n\nfunc file_diskerase_proto_rawDescGZIP() []byte {\n\tfile_diskerase_proto_rawDescOnce.Do(func() {\n\t\tfile_diskerase_proto_rawDescData = protoimpl.X.CompressGZIP(file_diskerase_proto_rawDescData)\n\t})\n\treturn file_diskerase_proto_rawDescData\n}\n\nvar file_diskerase_proto_enumTypes = make([]protoimpl.EnumInfo, 1)\nvar file_diskerase_proto_msgTypes = make([]protoimpl.MessageInfo, 12)\nvar file_diskerase_proto_goTypes = []interface{}{\n\t(Status)(0),         // 0: diskerase.Status\n\t(*WorkReq)(nil),     // 1: diskerase.WorkReq\n\t(*WorkResp)(nil),    // 2: diskerase.WorkResp\n\t(*Block)(nil),       // 3: diskerase.Block\n\t(*Job)(nil),         // 4: diskerase.Job\n\t(*ExecReq)(nil),     // 5: diskerase.ExecReq\n\t(*ExecResp)(nil),    // 6: diskerase.ExecResp\n\t(*StatusReq)(nil),   // 7: diskerase.StatusReq\n\t(*StatusResp)(nil),  // 8: diskerase.StatusResp\n\t(*BlockStatus)(nil), // 9: diskerase.BlockStatus\n\t(*JobStatus)(nil),   // 10: diskerase.JobStatus\n\tnil,                 // 11: diskerase.Job.ArgsEntry\n\tnil,                 // 12: diskerase.JobStatus.ArgsEntry\n}\nvar file_diskerase_proto_depIdxs = []int32{\n\t3,  // 0: diskerase.WorkReq.blocks:type_name -> diskerase.Block\n\t4,  // 1: diskerase.Block.jobs:type_name -> diskerase.Job\n\t11, // 2: diskerase.Job.args:type_name -> diskerase.Job.ArgsEntry\n\t0,  // 3: diskerase.StatusResp.status:type_name -> diskerase.Status\n\t9,  // 4: diskerase.StatusResp.blocks:type_name -> diskerase.BlockStatus\n\t0,  // 5: diskerase.BlockStatus.status:type_name -> diskerase.Status\n\t10, // 6: diskerase.BlockStatus.jobs:type_name -> diskerase.JobStatus\n\t12, // 7: diskerase.JobStatus.args:type_name -> diskerase.JobStatus.ArgsEntry\n\t0,  // 8: diskerase.JobStatus.status:type_name -> diskerase.Status\n\t1,  // 9: diskerase.Workflow.Submit:input_type -> diskerase.WorkReq\n\t5,  // 10: diskerase.Workflow.Exec:input_type -> diskerase.ExecReq\n\t7,  // 11: diskerase.Workflow.Status:input_type -> diskerase.StatusReq\n\t2,  // 12: diskerase.Workflow.Submit:output_type -> diskerase.WorkResp\n\t6,  // 13: diskerase.Workflow.Exec:output_type -> diskerase.ExecResp\n\t8,  // 14: diskerase.Workflow.Status:output_type -> diskerase.StatusResp\n\t12, // [12:15] is the sub-list for method output_type\n\t9,  // [9:12] is the sub-list for method input_type\n\t9,  // [9:9] is the sub-list for extension type_name\n\t9,  // [9:9] is the sub-list for extension extendee\n\t0,  // [0:9] is the sub-list for field type_name\n}\n\nfunc init() { file_diskerase_proto_init() }\nfunc file_diskerase_proto_init() {\n\tif File_diskerase_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_diskerase_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*WorkReq); 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_diskerase_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*WorkResp); 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_diskerase_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Block); 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_diskerase_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Job); 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_diskerase_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ExecReq); 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_diskerase_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ExecResp); 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_diskerase_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*StatusReq); 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_diskerase_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*StatusResp); 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_diskerase_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*BlockStatus); 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_diskerase_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*JobStatus); 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_diskerase_proto_rawDesc,\n\t\t\tNumEnums:      1,\n\t\t\tNumMessages:   12,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_diskerase_proto_goTypes,\n\t\tDependencyIndexes: file_diskerase_proto_depIdxs,\n\t\tEnumInfos:         file_diskerase_proto_enumTypes,\n\t\tMessageInfos:      file_diskerase_proto_msgTypes,\n\t}.Build()\n\tFile_diskerase_proto = out.File\n\tfile_diskerase_proto_rawDesc = nil\n\tfile_diskerase_proto_goTypes = nil\n\tfile_diskerase_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "chapter/16/workflow/proto/diskerase.proto",
    "content": "syntax = \"proto3\";\n\npackage diskerase;\n\noption go_package = \"github.com/PacktPublishing/Go-for-DevOps/chapter/18/diskerase/proto/diskerase\";\n\n// WorkReq is the definition of some work to be done by the system.\nmessage WorkReq {\n\t// This is used to describe the work to be done. This name\n\t// must be authorized by having a policy with the same name\n\t// in the server's policies.json fie.\n\tstring name = 1;\n\t// A description of what this is doing.\n\tstring desc = 2;\n\t// These are groupings of Jobs. Each block is executed one at\n\t// a time.\n\trepeated Block blocks = 3;\n}\n\n// WorkResp details the ID that will be used to refer to a submitted WorkReq.\nmessage WorkResp {\n\t// This is the unique ID for this WorkReq.\n\tstring id = 1;\n}\n\n// Block is a grouping of Jobs that will be executed concurrently\n// at some rate.\nmessage Block {\n\t// This describes what the Block is doing.\n\tstring desc = 1;\n\t// The amount of concurrency executions. < 1 will default to 1.\n\tint32 rate_limit = 2;\n\t// The Jobs to to execute in this Block.\n\trepeated Job jobs = 3;\n}\n\n// Job refers to a Job action that is defined on the server.\nmessage Job {\n\t// This is the name of the Job, which must be registered on\n\t// the server.\n\tstring name = 1;\n\t// This is a description of what the job is doing.\n\tstring desc = 2;\n\t// A mapping of key/value arguments. While the value is a string,\n\t// it can represent non-string data and will be converted by the\n\t// Job on the server. See the Job definition for a list of arguments\n\t// that are mandatory and optional.\n\tmap<string, string> args = 3;\n}\n\n// ExecReq is used to tell the server to execute a WorkReq\n// that was previously submitted.\nmessage ExecReq {\n\t// This is the unique ID of the WorkReq given back\n\t// by WorkResp.\n\tstring id = 1;\n}\n\n// ExecResp is the response from an ExecReq.\nmessage ExecResp {}\n\n// Status details the status of a Block or Job.\nenum Status {\n\t// Indicates that there is some bug\n\t// as the Status for the object was not set.\n\tStatusUnknown = 0;\n\t// The WorkReq, Block or Job has not started execution.\n\tStatusNotStarted = 1;\n\t// The WorkReq, Block or Job is currently executing.\n\tStatusRunning = 2;\n\t// The WorkReq, Block or Job has failed.\n\tStatusFailed = 3;\n\t// The WorkReq, Block or Job has completed.\n\tStatusCompleted = 4;\n}\n\n// StatusReq requests a status update from the server.\nmessage StatusReq {\n\t// The unique ID of the WorkReq.\n\tstring id = 1;\n}\n\n// StatusResp is the status of WorkReq.\nmessage StatusResp {\n\t// The name of the WorkReq.\n\tstring name = 1;\n\t// The description of the WorkReq.\n\tstring desc = 2; \n\t// The overall status of the WorkReq.\n\tStatus status = 3;\n\t// The status information on the Blocks.\n\trepeated BlockStatus blocks = 4;\n\t// If we are SatusFailed or StatusCompleted, if\n\t// there were any errors when run.\n\tbool had_errors = 5;\n\t// If the WorkReq was stopped with emergency stop.\n\tbool was_es_stopped = 6;\n}\n\n// BlockStatus holds the status of block execution.\nmessage BlockStatus {\n\t// The description of the block.\n\tstring desc = 1;\n\t// The status of the block.\n\tStatus status = 2;\n\t// If there any errors in Jobs in the Block.\n\tbool has_error = 3;\n\t// The status of Jobs in the Block.\n\trepeated JobStatus jobs = 4;\n}\n\n// JobStatus holds the status of the Jobs.\nmessage JobStatus {\n\t// The name of the Job called.\n\tstring name = 1;\n\t// The description of the Job.\n\tstring desc = 2;\n\t// The args for the Job.\n\tmap<string, string> args = 3;\n\t// The status of the Job.\n\tStatus status = 4;\n\t// The error, if there was one.\n\tstring error = 5;\n}\n\nservice Workflow {\n\t// Submit the work to the server. This will not execute the work, it will\n\t// simply verify it against policy and store it for execution.\n\trpc Submit(WorkReq) returns (WorkResp) {};\n\t// Tell the service to execute a WorkReq submitted earlier.\n\trpc Exec(ExecReq) returns (ExecResp) {};\n\t// Get the status of a WorkReq.\n\trpc Status(StatusReq) returns (StatusResp) {};\n}\n"
  },
  {
    "path": "chapter/16/workflow/proto/diskerase_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n\npackage diskerase\n\nimport (\n\tcontext \"context\"\n\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// WorkflowClient is the client API for Workflow 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 WorkflowClient interface {\n\t// Submit the work to the server. This will not execute the work, it will\n\t// simply verify it against policy and store it for execution.\n\tSubmit(ctx context.Context, in *WorkReq, opts ...grpc.CallOption) (*WorkResp, error)\n\t// Tell the service to execute a WorkReq submitted earlier.\n\tExec(ctx context.Context, in *ExecReq, opts ...grpc.CallOption) (*ExecResp, error)\n\t// Get the status of a WorkReq.\n\tStatus(ctx context.Context, in *StatusReq, opts ...grpc.CallOption) (*StatusResp, error)\n}\n\ntype workflowClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewWorkflowClient(cc grpc.ClientConnInterface) WorkflowClient {\n\treturn &workflowClient{cc}\n}\n\nfunc (c *workflowClient) Submit(ctx context.Context, in *WorkReq, opts ...grpc.CallOption) (*WorkResp, error) {\n\tout := new(WorkResp)\n\terr := c.cc.Invoke(ctx, \"/diskerase.Workflow/Submit\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *workflowClient) Exec(ctx context.Context, in *ExecReq, opts ...grpc.CallOption) (*ExecResp, error) {\n\tout := new(ExecResp)\n\terr := c.cc.Invoke(ctx, \"/diskerase.Workflow/Exec\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *workflowClient) Status(ctx context.Context, in *StatusReq, opts ...grpc.CallOption) (*StatusResp, error) {\n\tout := new(StatusResp)\n\terr := c.cc.Invoke(ctx, \"/diskerase.Workflow/Status\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// WorkflowServer is the server API for Workflow service.\n// All implementations must embed UnimplementedWorkflowServer\n// for forward compatibility\ntype WorkflowServer interface {\n\t// Submit the work to the server. This will not execute the work, it will\n\t// simply verify it against policy and store it for execution.\n\tSubmit(context.Context, *WorkReq) (*WorkResp, error)\n\t// Tell the service to execute a WorkReq submitted earlier.\n\tExec(context.Context, *ExecReq) (*ExecResp, error)\n\t// Get the status of a WorkReq.\n\tStatus(context.Context, *StatusReq) (*StatusResp, error)\n\tmustEmbedUnimplementedWorkflowServer()\n}\n\n// UnimplementedWorkflowServer must be embedded to have forward compatible implementations.\ntype UnimplementedWorkflowServer struct {\n}\n\nfunc (UnimplementedWorkflowServer) Submit(context.Context, *WorkReq) (*WorkResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method Submit not implemented\")\n}\nfunc (UnimplementedWorkflowServer) Exec(context.Context, *ExecReq) (*ExecResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method Exec not implemented\")\n}\nfunc (UnimplementedWorkflowServer) Status(context.Context, *StatusReq) (*StatusResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method Status not implemented\")\n}\nfunc (UnimplementedWorkflowServer) mustEmbedUnimplementedWorkflowServer() {}\n\n// UnsafeWorkflowServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to WorkflowServer will\n// result in compilation errors.\ntype UnsafeWorkflowServer interface {\n\tmustEmbedUnimplementedWorkflowServer()\n}\n\nfunc RegisterWorkflowServer(s grpc.ServiceRegistrar, srv WorkflowServer) {\n\ts.RegisterService(&Workflow_ServiceDesc, srv)\n}\n\nfunc _Workflow_Submit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(WorkReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(WorkflowServer).Submit(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/diskerase.Workflow/Submit\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(WorkflowServer).Submit(ctx, req.(*WorkReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Workflow_Exec_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ExecReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(WorkflowServer).Exec(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/diskerase.Workflow/Exec\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(WorkflowServer).Exec(ctx, req.(*ExecReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Workflow_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(StatusReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(WorkflowServer).Status(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/diskerase.Workflow/Status\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(WorkflowServer).Status(ctx, req.(*StatusReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// Workflow_ServiceDesc is the grpc.ServiceDesc for Workflow 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 Workflow_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"diskerase.Workflow\",\n\tHandlerType: (*WorkflowServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"Submit\",\n\t\t\tHandler:    _Workflow_Submit_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"Exec\",\n\t\t\tHandler:    _Workflow_Exec_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"Status\",\n\t\t\tHandler:    _Workflow_Status_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"diskerase.proto\",\n}\n"
  },
  {
    "path": "chapter/16/workflow/samples/diskerase/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright © 2021 John Doak\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\nall copies 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\nTHE SOFTWARE.\n"
  },
  {
    "path": "chapter/16/workflow/samples/diskerase/cmd/eraseSatellite.go",
    "content": "/*\nCopyright © 2021 John Doak\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\nall copies 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\nTHE SOFTWARE.\n*/\npackage cmd\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"sort\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/client\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/data/packages/sites\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/proto\"\n\n\t\"github.com/spf13/cobra\"\n)\n\nconst (\n\tsubmitLog = \"submit.log\"\n)\n\n// eraseSatelliteCmd represents the eraseSatellite command\nvar eraseSatelliteCmd = &cobra.Command{\n\tUse:   \"eraseSatellite\",\n\tShort: \"Erase all disks at a satellite datacenter\",\n\tLong: `Erase all disks at a satellite datacenter. The satellite must be in\nthe decom state\nand usage of using your command. For example:\n\nCobra is a CLI library for Go that empowers applications.\nThis application is a tool to generate the needed files\nto quickly create a Cobra application.`,\n\tRun: func(cmd *cobra.Command, args []string) {\n\t\tif len(args) != 1 {\n\t\t\tfmt.Printf(\"must pass a single arg, the name of the satellite datacenter\")\n\t\t\treturn\n\t\t}\n\t\tsat := args[0]\n\n\t\tfmt.Println(\"generating workflow...\")\n\t\twf, err := generateWork(sat)\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"problem generating workflow: %s\\n\", err)\n\t\t\treturn\n\t\t}\n\n\t\t// Open our attempt.log file to write our submissions\n\t\tf, err := os.OpenFile(submitLog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"could not open our submit.log file: %s\", err)\n\t\t\treturn\n\t\t}\n\n\t\tc, err := client.New(rootCmd.Flag(\"address\").Value.String())\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"could not connect to workflow service: %s\\n\", err)\n\t\t\treturn\n\t\t}\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\n\t\tdefer cancel()\n\n\t\tfmt.Println(\"submitting workflow...\")\n\t\tid, err := c.Submit(ctx, wf)\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"submission had an issue: %s\\n\", err)\n\t\t\treturn\n\t\t}\n\n\t\tif _, err := f.Write([]byte(\"\\n\" + id)); err != nil {\n\t\t\tfmt.Printf(\"could not write to our submit.log file: %s\", err)\n\t\t\treturn\n\t\t}\n\t\tf.Close()\n\n\t\tfmt.Printf(\"workflow(%s) accepted, ask server to execute workflow...\\n\", id)\n\n\t\t// Now execute our attempt.\n\t\terr = c.Exec(ctx, id)\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"executing workflow(%s) on the server had an issue: %s\\n\", id, err)\n\t\t\treturn\n\t\t}\n\t\tfmt.Printf(\"server is executing workflow(%s)\\n\", id)\n\n\t\tif err := monitor(context.Background(), c, id); err != nil {\n\t\t\tfmt.Printf(\"problem monitoring workflow(%s): %s\", id, err)\n\t\t\treturn\n\t\t}\n\t},\n}\n\nfunc init() {\n\trootCmd.AddCommand(eraseSatelliteCmd)\n}\n\n// generateWork takes in the satellite name, validates the satellite can have diskerase\n// called on it and finally generates the *pb.WorkReq that would be needed to erase that\n// satellite's machine's disk.\nfunc generateWork(sat string) (*pb.WorkReq, error) {\n\twf := &pb.WorkReq{\n\t\tName: \"SatelliteDiskErase\",\n\t\tDesc: \"Erasing disks in datacenter satellite \" + sat,\n\t}\n\n\tsite, ok := sites.Data.Sites[sat]\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"there is no datacenter called %q\", sat)\n\t}\n\n\tif site.Type != \"satellite\" {\n\t\treturn nil, fmt.Errorf(\"%q is not a satellite datacenter, it is a %s\", sat, site.Type)\n\t}\n\n\tif site.Status != \"decom\" {\n\t\treturn nil, fmt.Errorf(\"%q is not in the second state, was in %s\", sat, site.Status)\n\t}\n\n\t// Get a list of machines for the site, in alphabetical order.\n\tmachines := make([]sites.Machine, 0, len(site.Machines))\n\tfor _, m := range site.Machines {\n\t\tmachines = append(machines, m)\n\t}\n\tsort.SliceStable(\n\t\tmachines,\n\t\tfunc(i, j int) bool {\n\t\t\treturn site.Machines[i].Name < site.Machines[j].Name\n\t\t},\n\t)\n\n\t// This adds a top level block that validates the site is still in the decom state\n\t// and gets a token from the token bucket to do a satellite erasure.\n\tpreCond := &pb.Block{\n\t\tDesc: \"Check pre-conditions\",\n\t\tJobs: []*pb.Job{\n\t\t\t{\n\t\t\t\tName: \"validateDecom\",\n\t\t\t\tDesc: fmt.Sprintf(\"Validate satellite(%s) is in the decom state\", sat),\n\t\t\t\tArgs: map[string]string{\n\t\t\t\t\t\"site\": sat,\n\t\t\t\t\t\"type\": \"satellite\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"tokenBucket\",\n\t\t\t\tDesc: \"Get disk erase token, which limits our satellite decoms per hour\",\n\t\t\t\tArgs: map[string]string{\n\t\t\t\t\t\"bucket\": \"diskEraseSatellite\",\n\t\t\t\t\t\"fatal\":  \"true\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\twf.Blocks = append(wf.Blocks, preCond)\n\n\t// For ever set of 5 machines, build a block erasing those 5 machines.\n\t// Sleep at the end of the block for 1 minute.\n\t// Set the concurrency to 5 so that all disk erasures in a block happen at the same tiem.\n\tblock := &pb.Block{}\n\tfor i, m := range machines {\n\t\t// Every 5 machines, commit the block and start a new one.\n\t\tif i%5 == 0 && i != 0 {\n\t\t\tblock.Desc = getBlockDesc(block)\n\t\t\tblock.RateLimit = 5\n\t\t\tblock.Jobs = append(\n\t\t\t\tblock.Jobs,\n\t\t\t\t&pb.Job{\n\t\t\t\t\tName: \"sleep\",\n\t\t\t\t\tDesc: \"Wait 1 minute between disk erasures\",\n\t\t\t\t\tArgs: map[string]string{\n\t\t\t\t\t\t\"seconds\": \"60\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t)\n\t\t\twf.Blocks = append(wf.Blocks, block)\n\t\t\tblock = &pb.Block{}\n\t\t}\n\t\tblock.Jobs = append(\n\t\t\tblock.Jobs,\n\t\t\t&pb.Job{\n\t\t\t\tName: \"diskErase\",\n\t\t\t\tDesc: fmt.Sprintf(\"Erase satellite(%s) machine(%s) disk\", m.Site, m.Name),\n\t\t\t\tArgs: map[string]string{\n\t\t\t\t\t\"machine\": m.Name,\n\t\t\t\t\t\"site\":    m.Site,\n\t\t\t\t},\n\t\t\t},\n\t\t)\n\t}\n\t// If we have a block not committed, commit it. Dont' put a sleep afterwards.\n\tif len(block.Jobs) != 0 {\n\t\tblock.Desc = getBlockDesc(block)\n\t\tblock.RateLimit = 5\n\t\twf.Blocks = append(wf.Blocks, block)\n\t}\n\treturn wf, nil\n}\n\n// getBlockDesc writes a description of the block listing the range of\n// machines that are currently getting erased.\nfunc getBlockDesc(block *pb.Block) string {\n\tjobStart := block.Jobs[0].Args[\"machine\"]\n\tjobEnd := block.Jobs[len(block.Jobs)-1].Args[\"machine\"]\n\treturn fmt.Sprintf(\"disk erase machines %s-%s\", jobStart, jobEnd)\n}\n"
  },
  {
    "path": "chapter/16/workflow/samples/diskerase/cmd/protoStatus.go",
    "content": "/*\nCopyright © 2021 John Doak\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\nall copies 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\nTHE SOFTWARE.\n*/\npackage cmd\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/client\"\n\n\t\"github.com/fatih/color\"\n\t\"github.com/inancgumus/screen\"\n\t\"github.com/spf13/cobra\"\n\t\"google.golang.org/protobuf/encoding/protojson\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/proto\"\n)\n\n// protoStatusCmd represents the protoStatus command\nvar protoStatusCmd = &cobra.Command{\n\tUse:   \"protoStatus\",\n\tShort: \"Streams the status of a workflow in proto's JSON format until it ends\",\n\tLong: `If you have a workflow that you want to monitor the status of,\nthis will do that. It can be used for more than just diskerase, though\nit is primarly meant for that purpose. This differes from \"status\" in that it outputs\nin the full proto JSON format with all data.\n\nSimply pass the single argument, which is the ID of the workflow.`,\n\tRun: func(cmd *cobra.Command, args []string) {\n\t\tif len(args) != 1 {\n\t\t\tfmt.Printf(\"must pass a single arg, the ID of the workflow to monitor\")\n\t\t\treturn\n\t\t}\n\t\tc, err := client.New(rootCmd.Flag(\"address\").Value.String())\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"could not connect to workflow service: %s\\n\", err)\n\t\t\treturn\n\t\t}\n\t\tif err := monitorProto(context.Background(), c, args[0]); err != nil {\n\t\t\tfmt.Printf(\"had problem talking to server: %s\", err)\n\t\t\treturn\n\t\t}\n\t},\n}\n\nfunc init() {\n\trootCmd.AddCommand(protoStatusCmd)\n}\n\n// monitorProto will contact the server every 10 seconds until the workflow with \"id\"\n// has left the running state.\nfunc monitorProto(ctx context.Context, c *client.Workflow, id string) error {\n\tfor {\n\t\tresp, err := c.Status(ctx, id)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"problem getting status of ID(%s): %w\", id, err)\n\t\t}\n\t\tscreen.Clear()\n\t\tscreen.MoveTopLeft()\n\n\t\tcolor.New(color.FgRed).Println(\"Updates every 10 seconds\")\n\t\tfmt.Println(protojson.Format(resp))\n\t\tif resp.Status != pb.Status_StatusRunning {\n\t\t\tfmt.Println(\"Workflow completed!\")\n\t\t\treturn nil\n\t\t}\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn ctx.Err()\n\t\tcase <-time.After(10 * time.Second):\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "chapter/16/workflow/samples/diskerase/cmd/root.go",
    "content": "/*\nCopyright © 2021 John Doak\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\nall copies 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\nTHE SOFTWARE.\n*/\npackage cmd\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/spf13/viper\"\n)\n\nvar cfgFile string\n\n// rootCmd represents the base command when called without any subcommands\nvar rootCmd = &cobra.Command{\n\tUse:   \"diskerase\",\n\tShort: \"Execute or monitors a disk erase of a datacenter\",\n\tLong: `This application will execute or monitor a disk erase for a datacenter.\nCurrently it supports erasures at satellite datacenters only.\n`,\n}\n\n// Execute adds all child commands to the root command and sets flags appropriately.\n// This is called by main.main(). It only needs to happen once to the rootCmd.\nfunc Execute() {\n\tcobra.CheckErr(rootCmd.Execute())\n}\n\nfunc init() {\n\tcobra.OnInitialize(initConfig)\n\n\t// Here you will define your flags and configuration settings.\n\t// Cobra supports persistent flags, which, if defined here,\n\t// will be global for your application.\n\n\trootCmd.PersistentFlags().StringVar(&cfgFile, \"config\", \"\", \"config file (default is $HOME/.diskerase.yaml)\")\n\trootCmd.PersistentFlags().String(\"address\", \"127.0.0.1:8080\", \"the address the workflow server is at, host:port\")\n\n\t// Cobra also supports local flags, which will only run\n\t// when this action is called directly.\n\trootCmd.Flags().BoolP(\"toggle\", \"t\", false, \"Help message for toggle\")\n}\n\n// initConfig reads in config file and ENV variables if set.\nfunc initConfig() {\n\tif cfgFile != \"\" {\n\t\t// Use config file from the flag.\n\t\tviper.SetConfigFile(cfgFile)\n\t} else {\n\t\t// Find home directory.\n\t\thome, err := os.UserHomeDir()\n\t\tcobra.CheckErr(err)\n\n\t\t// Search config in home directory with name \".diskerase\" (without extension).\n\t\tviper.AddConfigPath(home)\n\t\tviper.SetConfigType(\"yaml\")\n\t\tviper.SetConfigName(\".diskerase\")\n\t}\n\n\tviper.AutomaticEnv() // read in environment variables that match\n\n\t// If a config file is found, read it in.\n\tif err := viper.ReadInConfig(); err == nil {\n\t\tfmt.Fprintln(os.Stderr, \"Using config file:\", viper.ConfigFileUsed())\n\t}\n}\n"
  },
  {
    "path": "chapter/16/workflow/samples/diskerase/cmd/status.go",
    "content": "/*\nCopyright © 2021 John Doak\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\nall copies 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\nTHE SOFTWARE.\n*/\npackage cmd\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/client\"\n\n\t\"github.com/fatih/color\"\n\t\"github.com/inancgumus/screen\"\n\t\"github.com/spf13/cobra\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/proto\"\n)\n\n// statusCmd represents the status command\nvar statusCmd = &cobra.Command{\n\tUse:   \"status\",\n\tShort: \"Streams the status of a worklow until it ends\",\n\tLong: `If you have a workflow that you want to monitor the status of,\nthis will do that. It can be used for more than just diskerase, though\nit is primarly meant for that purpose.\n\nSimply pass the single argument, which is the ID of the workflow.\n`,\n\tRun: func(cmd *cobra.Command, args []string) {\n\t\tif len(args) != 1 {\n\t\t\tfmt.Printf(\"must pass a single arg, the ID of the workflow to monitor\")\n\t\t\treturn\n\t\t}\n\t\tc, err := client.New(rootCmd.Flag(\"address\").Value.String())\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"could not connect to workflow service: %s\\n\", err)\n\t\t\treturn\n\t\t}\n\t\tif err := monitor(context.Background(), c, args[0]); err != nil {\n\t\t\tfmt.Printf(\"had problem talking to server: %s\", err)\n\t\t\treturn\n\t\t}\n\t},\n}\n\nfunc init() {\n\trootCmd.AddCommand(statusCmd)\n}\n\n// monitor will contact the server every 10 seconds until the workflow with \"id\"\n// has left the running state.\nfunc monitor(ctx context.Context, c *client.Workflow, id string) error {\n\tfor {\n\t\tresp, err := c.Status(ctx, id)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"problem getting status of ID(%s): %w\", id, err)\n\t\t}\n\t\tscreen.Clear()\n\t\tscreen.MoveTopLeft()\n\n\t\tcolor.New(color.FgRed).Println(\"Updates every 10 seconds\")\n\t\tfmt.Println(resp.CLISummary(id))\n\t\tif resp.Status != pb.Status_StatusRunning {\n\t\t\tfmt.Println(\"Workflow completed! To retrieve full details, use 'protoStatus' command.\")\n\t\t\treturn nil\n\t\t}\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn ctx.Err()\n\t\tcase <-time.After(10 * time.Second):\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "chapter/16/workflow/samples/diskerase/diskerase.go",
    "content": "/*\nCopyright © 2021 John Doak\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\nall copies 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\nTHE SOFTWARE.\n*/\npackage main\n\nimport (\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/data/packages/sites\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/samples/diskerase/cmd\"\n)\n\nfunc main() {\n\tsites.Init(\"../../data\")\n\tcmd.Execute()\n}\n"
  },
  {
    "path": "chapter/16/workflow/workflow.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/data/packages/sites\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/policy/config\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/service\"\n\t\"google.golang.org/grpc\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/proto\"\n\n\t// These register all our job types, as each has an init() that registers the Job with\n\t// the service. This is called a side effects import, because we don't actually use it.\n\t// The _ before the package indicates it will not be used directly.\n\t_ \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/service/jobs/register/diskerase\"\n\t_ \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/service/jobs/register/sleep\"\n\t_ \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/service/jobs/register/tokenbucket\"\n\t_ \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/service/jobs/register/validatedecom\"\n\n\t// These register all our policies, exactly like our Jobs work.\n\t_ \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/policy/register/restrictjobtypes\"\n\t_ \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/policy/register/sameargs\"\n\t_ \"github.com/PacktPublishing/Go-for-DevOps/chapter/16/workflow/internal/policy/register/startorend\"\n)\n\nvar (\n\taddr = flag.String(\"addr\", \"127.0.0.1:8080\", \"The address to run the server on\")\n)\n\n// dirMode is simply the mode we create our directories with.\nconst dirMode = os.ModeDir | 0700\n\nfunc main() {\n\tflag.Parse()\n\n\t// Read our policy config.\n\tconfig.Init()\n\tsites.Init(\"data\")\n\n\t// This makes sure we have a place to store workflows.\n\tp := filepath.Join(os.TempDir(), \"workflows\")\n\n\tstat, err := os.Stat(p)\n\tif err == nil {\n\t\tif !stat.IsDir() {\n\t\t\tpanic(p + \" is not a direcotry\")\n\t\t}\n\t\tif stat.Mode() != os.FileMode(dirMode) {\n\t\t\tpanic(fmt.Sprintf(\"%s is mode %v, not %v\", p, stat.Mode(), dirMode))\n\t\t}\n\t} else {\n\t\tif err := os.Mkdir(p, dirMode); err != nil {\n\t\t\tpanic(fmt.Sprintf(\"could not create directory(%s): %s\", p, err))\n\t\t}\n\t}\n\tlog.Println(\"Workflow Storage is at: \", p)\n\n\t// Create our implementation of the gRPC service.\n\tserv, err := service.New(p)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create a new gRPC service and register our implementation.\n\tg := grpc.NewServer()\n\tpb.RegisterWorkflowServer(g, serv)\n\n\t// Grab our address on the network and begin listening.\n\tlis, err := net.Listen(\"tcp\", *addr)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Tell gRPC to use our listener for new connections.\n\tlog.Println(\"Server started on: \", *addr)\n\tg.Serve(lis)\n}\n"
  },
  {
    "path": "chapter/5/excel/simple/excel.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"github.com/360EntSecGroup-Skylar/excelize\"\n)\n\nfunc main() {\n\txlsx := excelize.NewFile()\n\tsheet := \"Sheet1\"\n\n\txlsx.SetCellValue(sheet, \"A1\", \"Server Name\")\n\txlsx.SetCellValue(sheet, \"B1\", \"Generation\")\n\txlsx.SetCellValue(sheet, \"C1\", \"Acquisition Date\")\n\txlsx.SetCellValue(sheet, \"D1\", \"CPU Vendor\")\n\n\txlsx.SetCellValue(sheet, \"A2\", \"svlaa01\")\n\txlsx.SetCellValue(sheet, \"B2\", 12)\n\txlsx.SetCellValue(sheet, \"C2\", mustParse(\"10/27/2021\"))\n\txlsx.SetCellValue(sheet, \"D2\", \"Intel\")\n\txlsx.SetCellValue(sheet, \"A3\", \"svlac14\")\n\txlsx.SetCellValue(sheet, \"B3\", 13)\n\txlsx.SetCellValue(sheet, \"C3\", mustParse(\"12/13/2021\"))\n\txlsx.SetCellValue(sheet, \"D3\", \"AMD\")\n\n\t// Save xlsx file by the given path.\n\tif err := xlsx.SaveAs(\"./Book1.xlsx\"); err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc mustParse(s string) time.Time {\n\tconst layout = \"01/02/2006\"\n\n\tt, err := time.Parse(layout, s)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn t\n}\n"
  },
  {
    "path": "chapter/5/excel/visualization/excel.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/5/excel/visualization/internal/chart\"\n\n\t\"github.com/xuri/excelize/v2\"\n)\n\ntype CPUVendor string\n\nconst (\n\tUnknownCPUVendor CPUVendor = \"Unknown\"\n\tIntel            CPUVendor = \"Intel\"\n\tAMD              CPUVendor = \"AMD\"\n)\n\nvar validCPUVendors = map[CPUVendor]bool{\n\tIntel: true,\n\tAMD:   true,\n}\n\nfunc main() {\n\tsheet, err := newServerSheet()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tsheet.add(\"svlaa01\", 12, mustParse(\"10/27/2021\"), Intel)\n\tsheet.add(\"svlac14\", 13, mustParse(\"12/13/2021\"), AMD)\n\n\tif err := sheet.render(); err != nil {\n\t\tpanic(err)\n\t}\n}\n\ntype summaries struct {\n\tcpuVendor cpuVendorSum\n}\n\ntype cpuVendorSum struct {\n\tunknown, intel, amd int\n}\n\ntype serverSheet struct {\n\tmu        sync.Mutex\n\tsheetName string\n\txlsx      *excelize.File\n\tsummaries *summaries\n\tnextRow   int\n}\n\nfunc newServerSheet() (*serverSheet, error) {\n\ts := &serverSheet{\n\t\tsheetName: \"Sheet1\",\n\t\txlsx:      excelize.NewFile(),\n\t\tsummaries: &summaries{},\n\t\tnextRow:   2,\n\t}\n\n\ts.xlsx.SetCellValue(s.sheetName, \"A1\", \"Server Name\")\n\ts.xlsx.SetCellValue(s.sheetName, \"B1\", \"Generation\")\n\ts.xlsx.SetCellValue(s.sheetName, \"C1\", \"Acquisition Date\")\n\ts.xlsx.SetCellValue(s.sheetName, \"D1\", \"CPU Vendor\")\n\n\treturn s, nil\n}\n\nfunc (s *serverSheet) add(name string, gen int, acquisition time.Time, vendor CPUVendor) error {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\n\tif name == \"\" {\n\t\treturn errors.New(\"name cannot be blank\")\n\t}\n\tif gen < 1 || gen > 13 {\n\t\treturn errors.New(\"gen was not between 1 and including 13\")\n\t}\n\tif acquisition.IsZero() {\n\t\treturn errors.New(\"acquisition cannot be the zero time\")\n\t}\n\tif !validCPUVendors[vendor] {\n\t\treturn fmt.Errorf(\"vendor %v is not a valid vendor\", vendor)\n\t}\n\n\ts.xlsx.SetCellValue(s.sheetName, \"A\"+strconv.Itoa(s.nextRow), name)\n\ts.xlsx.SetCellValue(s.sheetName, \"B\"+strconv.Itoa(s.nextRow), gen)\n\ts.xlsx.SetCellValue(s.sheetName, \"C\"+strconv.Itoa(s.nextRow), acquisition)\n\ts.xlsx.SetColWidth(s.sheetName, \"C\", \"C\", 20)\n\ts.xlsx.SetCellValue(s.sheetName, \"D\"+strconv.Itoa(s.nextRow), vendor)\n\tswitch vendor {\n\tcase Intel:\n\t\ts.summaries.cpuVendor.intel++\n\tcase AMD:\n\t\ts.summaries.cpuVendor.amd++\n\tdefault:\n\t\ts.summaries.cpuVendor.unknown++\n\t}\n\ts.nextRow++\n\n\treturn nil\n}\n\nfunc (s *serverSheet) render() error {\n\ts.writeSummaries()\n\n\tif err := s.createCPUChart(); err != nil {\n\t\treturn fmt.Errorf(\"problem creating CPU chart: %w\", err)\n\t}\n\n\treturn s.xlsx.SaveAs(\"./Book1.xlsx\")\n}\n\nfunc (s *serverSheet) writeSummaries() {\n\ts.xlsx.SetCellValue(s.sheetName, \"F1\", \"Vendor Summary\")\n\ts.xlsx.SetCellValue(s.sheetName, \"F2\", \"Vendor\")\n\ts.xlsx.SetCellValue(s.sheetName, \"G2\", \"Total\")\n\n\ts.xlsx.SetCellValue(s.sheetName, \"F3\", Intel)\n\ts.xlsx.SetCellValue(s.sheetName, \"G3\", s.summaries.cpuVendor.intel)\n\ts.xlsx.SetCellValue(s.sheetName, \"F4\", AMD)\n\ts.xlsx.SetCellValue(s.sheetName, \"G4\", s.summaries.cpuVendor.amd)\n}\n\nfunc (s *serverSheet) createCPUChart() error {\n\tc := chart.New()\n\tc.Type = \"pie3D\"\n\tc.Dimension = chart.FormatChartDimension{640, 480}\n\tc.Title = chart.FormatChartTitle{Name: \"Server CPU Vendor Breakdown\"}\n\tc.Format = chart.FormatPicture{\n\t\tFPrintsWithSheet: true,\n\t\tNoChangeAspect:   false,\n\t\tFLocksWithSheet:  false,\n\t\tOffsetX:          15,\n\t\tOffsetY:          10,\n\t\tXScale:           1.0,\n\t\tYScale:           1.0,\n\t}\n\tc.Legend = chart.FormatChartLegend{\n\t\tPosition:      \"bottom\",\n\t\tShowLegendKey: true,\n\t}\n\tc.Plotarea.ShowBubbleSize = true\n\tc.Plotarea.ShowCatName = true\n\tc.Plotarea.ShowLeaderLines = false\n\tc.Plotarea.ShowPercent = true\n\tc.Plotarea.ShowSerName = true\n\tc.ShowBlanksAs = \"zero\"\n\n\tc.Series = append(\n\t\tc.Series,\n\t\tchart.FormatChartSeries{\n\t\t\tName:       `%s!$F$1`,\n\t\t\tCategories: fmt.Sprintf(`%s!$F$3:$F$4`, s.sheetName),\n\t\t\tValues:     fmt.Sprintf(`%s!$G$3:$G$4`, s.sheetName),\n\t\t},\n\t)\n\n\tb, err := json.Marshal(c)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := s.xlsx.AddChart(s.sheetName, \"I1\", string(b)); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc mustParse(s string) time.Time {\n\tconst layout = \"01/02/2006\"\n\n\tt, err := time.Parse(layout, s)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn t\n}\n"
  },
  {
    "path": "chapter/5/excel/visualization/internal/chart/chart.go",
    "content": "package chart\n\n// New creates a FormatChart with default values.\nfunc New() *FormatChart {\n\treturn &FormatChart{\n\t\tDimension: FormatChartDimension{\n\t\t\tWidth:  480,\n\t\t\tHeight: 290,\n\t\t},\n\t\tFormat: FormatPicture{\n\t\t\tFPrintsWithSheet: true,\n\t\t\tFLocksWithSheet:  false,\n\t\t\tNoChangeAspect:   false,\n\t\t\tOffsetX:          0,\n\t\t\tOffsetY:          0,\n\t\t\tXScale:           1.0,\n\t\t\tYScale:           1.0,\n\t\t},\n\t\tLegend: FormatChartLegend{\n\t\t\tPosition:      \"bottom\",\n\t\t\tShowLegendKey: false,\n\t\t},\n\t\tTitle: FormatChartTitle{\n\t\t\tName: \" \",\n\t\t},\n\t\tShowBlanksAs: \"gap\",\n\t}\n}\n\n// FormatChartAxis directly maps the Format settings of the chart axis.\ntype FormatChartAxis struct {\n\tCrossing            string  `json:\"crossing\"`\n\tMajorTickMark       string  `json:\"major_tick_mark\"`\n\tMinorTickMark       string  `json:\"minor_tick_mark\"`\n\tMinorUnitType       string  `json:\"minor_unit_type\"`\n\tMajorUnit           int     `json:\"major_unit\"`\n\tMajorUnitType       string  `json:\"major_unit_type\"`\n\tDisplayUnits        string  `json:\"display_units\"`\n\tDisplayUnitsVisible bool    `json:\"display_units_visible\"`\n\tDateAxis            bool    `json:\"date_axis\"`\n\tReverseOrder        bool    `json:\"reverse_order\"`\n\tMaximum             float64 `json:\"maximum\"`\n\tMinimum             float64 `json:\"minimum\"`\n\tNumFormat           string  `json:\"num_Format\"`\n\tNumFont             struct {\n\t\tColor     string `json:\"color\"`\n\t\tBold      bool   `json:\"bold\"`\n\t\tItalic    bool   `json:\"italic\"`\n\t\tUnderline bool   `json:\"underline\"`\n\t} `json:\"num_font\"`\n\tNameLayout FormatLayout `json:\"name_layout\"`\n}\n\ntype FormatChartDimension struct {\n\tWidth  int `json:\"width\"`\n\tHeight int `json:\"height\"`\n}\n\ntype FormatChart struct {\n\tType      string               `json:\"type\"`\n\tSeries    []FormatChartSeries  `json:\"series\"`\n\tFormat    FormatPicture        `json:\"Format\"`\n\tDimension FormatChartDimension `json:\"dimension\"`\n\tLegend    FormatChartLegend    `json:\"legend\"`\n\tTitle     FormatChartTitle     `json:\"title\"`\n\tXAxis     FormatChartAxis      `json:\"x_axis\"`\n\tYAxis     FormatChartAxis      `json:\"y_axis\"`\n\tChartarea struct {\n\t\tBorder struct {\n\t\t\tNone bool `json:\"none\"`\n\t\t} `json:\"border\"`\n\t\tFill struct {\n\t\t\tColor string `json:\"color\"`\n\t\t} `json:\"fill\"`\n\t\tPattern struct {\n\t\t\tPattern string `json:\"pattern\"`\n\t\t\tFgColor string `json:\"fg_color\"`\n\t\t\tBgColor string `json:\"bg_color\"`\n\t\t} `json:\"pattern\"`\n\t} `json:\"chartarea\"`\n\tPlotarea struct {\n\t\tShowBubbleSize  bool `json:\"show_bubble_size\"`\n\t\tShowCatName     bool `json:\"show_cat_name\"`\n\t\tShowLeaderLines bool `json:\"show_leader_lines\"`\n\t\tShowPercent     bool `json:\"show_percent\"`\n\t\tShowSerName     bool `json:\"show_series_name\"`\n\t\tShowVal         bool `json:\"show_val\"`\n\t\tGradient        struct {\n\t\t\tColors []string `json:\"colors\"`\n\t\t} `json:\"gradient\"`\n\t\tBorder struct {\n\t\t\tColor    string `json:\"color\"`\n\t\t\tWidth    int    `json:\"width\"`\n\t\t\tDashType string `json:\"dash_type\"`\n\t\t} `json:\"border\"`\n\t\tFill struct {\n\t\t\tColor string `json:\"color\"`\n\t\t} `json:\"fill\"`\n\t\tLayout FormatLayout `json:\"layout\"`\n\t} `json:\"plotarea\"`\n\tShowBlanksAs   string `json:\"show_blanks_as\"`\n\tShowHiddenData bool   `json:\"show_hidden_data\"`\n\tSetRotation    int    `json:\"set_rotation\"`\n\tSetHoleSize    int    `json:\"set_hole_size\"`\n}\n\n// FormatChartLegend directly maps the Format settings of the chart legend.\ntype FormatChartLegend struct {\n\tNone            bool         `json:\"none\"`\n\tDeleteSeries    []int        `json:\"delete_series\"`\n\tFont            FormatFont   `json:\"font\"`\n\tLayout          FormatLayout `json:\"layout\"`\n\tPosition        string       `json:\"position\"`\n\tShowLegendEntry bool         `json:\"show_legend_entry\"`\n\tShowLegendKey   bool         `json:\"show_legend_key\"`\n}\n\n// FormatChartSeries directly maps the Format settings of the chart series.\ntype FormatChartSeries struct {\n\tName       string `json:\"name\"`\n\tCategories string `json:\"categories\"`\n\tValues     string `json:\"values\"`\n\tLine       struct {\n\t\tNone  bool   `json:\"none\"`\n\t\tColor string `json:\"color\"`\n\t} `json:\"line\"`\n\tMarker struct {\n\t\tType   string  `json:\"type\"`\n\t\tSize   int     `json:\"size,\"`\n\t\tWidth  float64 `json:\"width\"`\n\t\tBorder struct {\n\t\t\tColor string `json:\"color\"`\n\t\t\tNone  bool   `json:\"none\"`\n\t\t} `json:\"border\"`\n\t\tFill struct {\n\t\t\tColor string `json:\"color\"`\n\t\t\tNone  bool   `json:\"none\"`\n\t\t} `json:\"fill\"`\n\t} `json:\"marker\"`\n}\n\n// FormatChartTitle directly maps the Format settings of the chart title.\ntype FormatChartTitle struct {\n\tNone    bool         `json:\"none\"`\n\tName    string       `json:\"name\"`\n\tOverlay bool         `json:\"overlay\"`\n\tLayout  FormatLayout `json:\"layout\"`\n}\n\n// FormatLayout directly maps the Format settings of the element layout.\ntype FormatLayout struct {\n\tX      float64 `json:\"x\"`\n\tY      float64 `json:\"y\"`\n\tWidth  float64 `json:\"width\"`\n\tHeight float64 `json:\"height\"`\n}\n\n// FormatPicture directly maps the Format settings of the picture.\ntype FormatPicture struct {\n\tFPrintsWithSheet bool    `json:\"print_obj\"`\n\tFLocksWithSheet  bool    `json:\"locked\"`\n\tNoChangeAspect   bool    `json:\"lock_aspect_ratio\"`\n\tOffsetX          int     `json:\"x_offset\"`\n\tOffsetY          int     `json:\"y_offset\"`\n\tXScale           float64 `json:\"x_scale\"`\n\tYScale           float64 `json:\"y_scale\"`\n\tHyperlink        string  `json:\"hyperlink\"`\n\tHyperlinkType    string  `json:\"hyperlink_type\"`\n\tPositioning      string  `json:\"positioning\"`\n}\n\n// FormatShape directly maps the Format settings of the shape.\ntype FormatShape struct {\n\tType      string                 `json:\"type\"`\n\tWidth     int                    `json:\"width\"`\n\tHeight    int                    `json:\"height\"`\n\tFormat    FormatPicture          `json:\"Format\"`\n\tColor     FormatShapeColor       `json:\"color\"`\n\tParagraph []FormatShapeParagraph `json:\"paragraph\"`\n}\n\n// FormatShapeParagraph directly maps the Format settings of the paragraph in\n// the shape.\ntype FormatShapeParagraph struct {\n\tFont FormatFont `json:\"font\"`\n\tText string     `json:\"text\"`\n}\n\n// FormatShapeColor directly maps the color settings of the shape.\ntype FormatShapeColor struct {\n\tLine   string `json:\"line\"`\n\tFill   string `json:\"fill\"`\n\tEffect string `json:\"effect\"`\n}\n\n// FormatFont directly maps the styles settings of the fonts.\ntype FormatFont struct {\n\tBold      bool   `json:\"bold\"`\n\tItalic    bool   `json:\"italic\"`\n\tUnderline string `json:\"underline\"`\n\tFamily    string `json:\"family\"`\n\tSize      int    `json:\"size\"`\n\tColor     string `json:\"color\"`\n}\n\n// FormatStyle directly maps the styles settings of the cells.\ntype FormatStyle struct {\n\tBorder []struct {\n\t\tType  string `json:\"type\"`\n\t\tColor string `json:\"color\"`\n\t\tStyle int    `json:\"style\"`\n\t} `json:\"border\"`\n\tFill struct {\n\t\tType    string   `json:\"type\"`\n\t\tPattern int      `json:\"pattern\"`\n\t\tColor   []string `json:\"color\"`\n\t\tShading int      `json:\"shading\"`\n\t} `json:\"fill\"`\n\tFont      *FormatFont `json:\"font\"`\n\tAlignment *struct {\n\t\tHorizontal      string `json:\"horizontal\"`\n\t\tIndent          int    `json:\"indent\"`\n\t\tJustifyLastLine bool   `json:\"justify_last_line\"`\n\t\tReadingOrder    uint64 `json:\"reading_order\"`\n\t\tRelativeIndent  int    `json:\"relative_indent\"`\n\t\tShrinkToFit     bool   `json:\"shrink_to_fit\"`\n\t\tTextRotation    int    `json:\"text_rotation\"`\n\t\tVertical        string `json:\"vertical\"`\n\t\tWrapText        bool   `json:\"wrap_text\"`\n\t} `json:\"alignment\"`\n\tProtection *struct {\n\t\tHidden bool `json:\"hidden\"`\n\t\tLocked bool `json:\"locked\"`\n\t} `json:\"protection\"`\n\tNumFmt        int     `json:\"number_Format\"`\n\tDecimalPlaces int     `json:\"decimal_places\"`\n\tCustomNumFmt  *string `json:\"custom_number_Format\"`\n\tLang          string  `json:\"lang\"`\n\tNegRed        bool    `json:\"negred\"`\n}\n"
  },
  {
    "path": "chapter/6/grpc/buf.work",
    "content": "version: v1\ndirectories:\n  - proto\n"
  },
  {
    "path": "chapter/6/grpc/client/bin/qotd.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"fmt\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/6/grpc/client\"\n)\n\nvar (\n\taddr   = flag.String(\"addr\", \"127.0.0.1:80\", \"The address of the server.\")\n\tauthor = flag.String(\"author\", \"\", \"The author whose quote to get\")\n)\n\nfunc main() {\n\tflag.Parse()\n\n\tc, err := client.New(*addr)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\ta, q, err := c.QOTD(context.Background(), *author)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(\"Author: \", a)\n\tfmt.Printf(\"Quote of the Day: %q\\n\", q)\n}\n"
  },
  {
    "path": "chapter/6/grpc/client/client.go",
    "content": "package client\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"google.golang.org/grpc\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/6/grpc/proto\"\n)\n\n// Client is a client to the Quote of the day server.\ntype Client struct {\n\tclient pb.QOTDClient\n\tconn   *grpc.ClientConn\n}\n\n// New is the constructor for Client. addr is the server's [host]:[port].\nfunc New(addr string) (*Client, error) {\n\tconn, err := grpc.Dial(addr, grpc.WithInsecure())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Client{\n\t\tclient: pb.NewQOTDClient(conn),\n\t\tconn:   conn,\n\t}, nil\n}\n\n// QOTD retrieves a quote of the day. If wantAuthor is not set, will randomly choose the author\n// of a quote.\nfunc (c *Client) QOTD(ctx context.Context, wantAuthor string) (author, quote string, err error) {\n\tif _, ok := ctx.Deadline(); !ok {\n\t\tvar cancel context.CancelFunc\n\t\tctx, cancel = context.WithTimeout(ctx, 2*time.Second)\n\t\tdefer cancel()\n\t}\n\n\tresp, err := c.client.GetQOTD(ctx, &pb.GetReq{Author: wantAuthor})\n\tif err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\treturn resp.Author, resp.Quote, nil\n}\n"
  },
  {
    "path": "chapter/6/grpc/proto/buf.gen.yaml",
    "content": "version: v1\nplugins:\n  - name: go\n    out: ./\n    opt:\n      - paths=source_relative\n  - name: go-grpc\n    out: ./\n    opt:\n      - paths=source_relative\n"
  },
  {
    "path": "chapter/6/grpc/proto/buf.yaml",
    "content": "version: v1\nlint:\n  use:\n    - DEFAULT\nbreaking:\n  use:\n    - FILE\n"
  },
  {
    "path": "chapter/6/grpc/proto/qotd.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.25.0-devel\n// \tprotoc        v3.18.0\n// source: qotd.proto\n\npackage qotd\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 GetReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tAuthor string `protobuf:\"bytes,1,opt,name=author,proto3\" json:\"author,omitempty\"`\n}\n\nfunc (x *GetReq) Reset() {\n\t*x = GetReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_qotd_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GetReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GetReq) ProtoMessage() {}\n\nfunc (x *GetReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_qotd_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 GetReq.ProtoReflect.Descriptor instead.\nfunc (*GetReq) Descriptor() ([]byte, []int) {\n\treturn file_qotd_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *GetReq) GetAuthor() string {\n\tif x != nil {\n\t\treturn x.Author\n\t}\n\treturn \"\"\n}\n\ntype GetResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tAuthor string `protobuf:\"bytes,1,opt,name=author,proto3\" json:\"author,omitempty\"`\n\tQuote  string `protobuf:\"bytes,2,opt,name=quote,proto3\" json:\"quote,omitempty\"`\n}\n\nfunc (x *GetResp) Reset() {\n\t*x = GetResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_qotd_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GetResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GetResp) ProtoMessage() {}\n\nfunc (x *GetResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_qotd_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 GetResp.ProtoReflect.Descriptor instead.\nfunc (*GetResp) Descriptor() ([]byte, []int) {\n\treturn file_qotd_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *GetResp) GetAuthor() string {\n\tif x != nil {\n\t\treturn x.Author\n\t}\n\treturn \"\"\n}\n\nfunc (x *GetResp) GetQuote() string {\n\tif x != nil {\n\t\treturn x.Quote\n\t}\n\treturn \"\"\n}\n\nvar File_qotd_proto protoreflect.FileDescriptor\n\nvar file_qotd_proto_rawDesc = []byte{\n\t0x0a, 0x0a, 0x71, 0x6f, 0x74, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x71, 0x6f,\n\t0x74, 0x64, 0x22, 0x20, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06,\n\t0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x75,\n\t0x74, 0x68, 0x6f, 0x72, 0x22, 0x37, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12,\n\t0x16, 0x0a, 0x06, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,\n\t0x06, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x6f, 0x74, 0x65,\n\t0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x32, 0x30, 0x0a,\n\t0x04, 0x51, 0x4f, 0x54, 0x44, 0x12, 0x28, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x51, 0x4f, 0x54, 0x44,\n\t0x12, 0x0c, 0x2e, 0x71, 0x6f, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x0d,\n\t0x2e, 0x71, 0x6f, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x42,\n\t0x1e, 0x5a, 0x1c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x5b, 0x72,\n\t0x65, 0x70, 0x6f, 0x5d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x71, 0x6f, 0x74, 0x64, 0x62,\n\t0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_qotd_proto_rawDescOnce sync.Once\n\tfile_qotd_proto_rawDescData = file_qotd_proto_rawDesc\n)\n\nfunc file_qotd_proto_rawDescGZIP() []byte {\n\tfile_qotd_proto_rawDescOnce.Do(func() {\n\t\tfile_qotd_proto_rawDescData = protoimpl.X.CompressGZIP(file_qotd_proto_rawDescData)\n\t})\n\treturn file_qotd_proto_rawDescData\n}\n\nvar file_qotd_proto_msgTypes = make([]protoimpl.MessageInfo, 2)\nvar file_qotd_proto_goTypes = []interface{}{\n\t(*GetReq)(nil),  // 0: qotd.GetReq\n\t(*GetResp)(nil), // 1: qotd.GetResp\n}\nvar file_qotd_proto_depIdxs = []int32{\n\t0, // 0: qotd.QOTD.GetQOTD:input_type -> qotd.GetReq\n\t1, // 1: qotd.QOTD.GetQOTD:output_type -> qotd.GetResp\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_qotd_proto_init() }\nfunc file_qotd_proto_init() {\n\tif File_qotd_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_qotd_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*GetReq); 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_qotd_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*GetResp); 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_qotd_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_qotd_proto_goTypes,\n\t\tDependencyIndexes: file_qotd_proto_depIdxs,\n\t\tMessageInfos:      file_qotd_proto_msgTypes,\n\t}.Build()\n\tFile_qotd_proto = out.File\n\tfile_qotd_proto_rawDesc = nil\n\tfile_qotd_proto_goTypes = nil\n\tfile_qotd_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "chapter/6/grpc/proto/qotd.proto",
    "content": "syntax = \"proto3\";\npackage qotd;\n\noption go_package = \"github.com/[repo]/proto/qotd\";\n\nmessage GetReq {\n\tstring author = 1;\n}\n\nmessage GetResp {\n\tstring author = 1;\n\tstring quote = 2;\n}\n\nservice QOTD {\n   rpc GetQOTD(GetReq) returns (GetResp) {};\n}\n"
  },
  {
    "path": "chapter/6/grpc/proto/qotd_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n\npackage qotd\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// QOTDClient is the client API for QOTD 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 QOTDClient interface {\n\tGetQOTD(ctx context.Context, in *GetReq, opts ...grpc.CallOption) (*GetResp, error)\n}\n\ntype qOTDClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewQOTDClient(cc grpc.ClientConnInterface) QOTDClient {\n\treturn &qOTDClient{cc}\n}\n\nfunc (c *qOTDClient) GetQOTD(ctx context.Context, in *GetReq, opts ...grpc.CallOption) (*GetResp, error) {\n\tout := new(GetResp)\n\terr := c.cc.Invoke(ctx, \"/qotd.QOTD/GetQOTD\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// QOTDServer is the server API for QOTD service.\n// All implementations must embed UnimplementedQOTDServer\n// for forward compatibility\ntype QOTDServer interface {\n\tGetQOTD(context.Context, *GetReq) (*GetResp, error)\n\tmustEmbedUnimplementedQOTDServer()\n}\n\n// UnimplementedQOTDServer must be embedded to have forward compatible implementations.\ntype UnimplementedQOTDServer struct {\n}\n\nfunc (UnimplementedQOTDServer) GetQOTD(context.Context, *GetReq) (*GetResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method GetQOTD not implemented\")\n}\nfunc (UnimplementedQOTDServer) mustEmbedUnimplementedQOTDServer() {}\n\n// UnsafeQOTDServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to QOTDServer will\n// result in compilation errors.\ntype UnsafeQOTDServer interface {\n\tmustEmbedUnimplementedQOTDServer()\n}\n\nfunc RegisterQOTDServer(s grpc.ServiceRegistrar, srv QOTDServer) {\n\ts.RegisterService(&QOTD_ServiceDesc, srv)\n}\n\nfunc _QOTD_GetQOTD_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(GetReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(QOTDServer).GetQOTD(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/qotd.QOTD/GetQOTD\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(QOTDServer).GetQOTD(ctx, req.(*GetReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// QOTD_ServiceDesc is the grpc.ServiceDesc for QOTD 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 QOTD_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"qotd.QOTD\",\n\tHandlerType: (*QOTDServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"GetQOTD\",\n\t\t\tHandler:    _QOTD_GetQOTD_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"qotd.proto\",\n}\n"
  },
  {
    "path": "chapter/6/grpc/qotd.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"log\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/6/grpc/server\"\n)\n\nvar addr = flag.String(\"addr\", \"127.0.0.1:80\", \"The address to run on.\")\n\nfunc main() {\n\tflag.Parse()\n\n\ts, err := server.New(*addr)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tdone := make(chan error, 1)\n\n\tlog.Println(\"Starting server at: \", *addr)\n\tgo func() {\n\t\tdefer close(done)\n\t\tdone <- s.Start()\n\t}()\n\n\terr = <-done\n\tlog.Println(\"Server exited with error: \", err)\n}\n"
  },
  {
    "path": "chapter/6/grpc/server/server.go",
    "content": "package server\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"net\"\n\t\"sync\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/6/grpc/proto\"\n)\n\ntype API struct {\n\tpb.UnimplementedQOTDServer\n\n\taddr   string\n\tquotes map[string][]string\n\n\tmu         sync.Mutex\n\tgrpcServer *grpc.Server\n}\n\nfunc New(addr string) (*API, error) {\n\tvar opts []grpc.ServerOption\n\n\ta := &API{\n\t\taddr: addr,\n\t\tquotes: map[string][]string{\n\t\t\t\"Mark Twain\": {\n\t\t\t\t\"History doesn't repeat itself, but it does rhyme\",\n\t\t\t\t\"Lies, damned lies, and statistics\",\n\t\t\t\t\"Golf is a good walk spoiled\",\n\t\t\t},\n\t\t\t\"Benjamin Franklin\": {\n\t\t\t\t\"Tell me and I forget. Teach me and I remember. Involve me and I learn\",\n\t\t\t\t\"I didn't fail the test. I just found 100 ways to do it wrong\",\n\t\t\t},\n\t\t\t\"Eleanor Roosevelt\": {\n\t\t\t\t\"The future belongs to those who believe in the beauty of their dreams\",\n\t\t\t},\n\t\t},\n\t\tgrpcServer: grpc.NewServer(opts...),\n\t}\n\ta.grpcServer.RegisterService(&pb.QOTD_ServiceDesc, a)\n\n\treturn a, nil\n}\n\nfunc (a *API) Start() error {\n\ta.mu.Lock()\n\tdefer a.mu.Unlock()\n\n\tlis, err := net.Listen(\"tcp\", a.addr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn a.grpcServer.Serve(lis)\n}\n\nfunc (a *API) Stop() {\n\ta.mu.Lock()\n\tdefer a.mu.Unlock()\n\n\ta.grpcServer.Stop()\n}\n\nfunc (a *API) GetQOTD(ctx context.Context, req *pb.GetReq) (*pb.GetResp, error) {\n\tvar (\n\t\tauthor string\n\t\tquotes []string\n\t)\n\n\tif req.Author == \"\" {\n\t\tfor author, quotes = range a.quotes {\n\t\t\tbreak\n\t\t}\n\t} else {\n\t\tauthor = req.Author\n\t\tvar ok bool\n\t\tquotes, ok = a.quotes[req.Author]\n\t\tif !ok {\n\t\t\treturn nil, status.Error(codes.NotFound, fmt.Sprintf(\"author %q not found\", req.Author))\n\t\t}\n\t}\n\n\treturn &pb.GetResp{\n\t\tAuthor: author,\n\t\tQuote:  quotes[rand.Intn(len(quotes))],\n\t}, nil\n}\n"
  },
  {
    "path": "chapter/7/cobra/app/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright © 2021 John Doak\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\nall copies 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\nTHE SOFTWARE.\n"
  },
  {
    "path": "chapter/7/cobra/app/cmd/get.go",
    "content": "/*\nCopyright © 2021 John Doak\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\nall copies 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\nTHE SOFTWARE.\n*/\npackage cmd\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/6/grpc/client\"\n\n\t\"github.com/spf13/cobra\"\n\t\"github.com/spf13/pflag\"\n)\n\n// getCmd represents the get command\nvar getCmd = &cobra.Command{\n\tUse:   \"get\",\n\tShort: \"Fetches a quote of the day from the QOTD server\",\n\tLong: `This command allows you to fetch a quote of the day from our\nQOTD server we designed in our chapter on gRPC. This command defaults to a\nproduction server (which doesn't exist). This can be changed to the devlopement\nserver (which doesn't exist) using --dev or to a specific address with --addr .\n\nExample usage for a random author:\nqotd get\n\nExample usage for a specific author:\nqotd get --author=\"mark twain\"\n\nExample usage using a 127.0.0.1 for the server:\nqotd get -addr=127.0.0.1:80 -author=\"mark twain\"\n`,\n\tRun: func(cmd *cobra.Command, args []string) {\n\t\tconst devAddr = \"127.0.0.1:3450\"\n\n\t\tfs := cmd.Flags()\n\n\t\taddr := mustString(fs, \"addr\")\n\n\t\tif mustBool(fs, \"dev\") {\n\t\t\taddr = devAddr\n\t\t}\n\n\t\tc, err := client.New(addr)\n\t\tif err != nil {\n\t\t\tfmt.Println(\"error: \", err)\n\t\t\tos.Exit(1)\n\t\t}\n\n\t\ta, q, err := c.QOTD(cmd.Context(), mustString(fs, \"author\"))\n\t\tif err != nil {\n\t\t\tfmt.Println(\"error: \", err)\n\t\t\tos.Exit(1)\n\t\t}\n\n\t\tswitch {\n\t\tcase mustBool(fs, \"json\"):\n\t\t\tb, err := json.Marshal(\n\t\t\t\tstruct {\n\t\t\t\t\tAuthor string\n\t\t\t\t\tQuote  string\n\t\t\t\t}{a, q},\n\t\t\t)\n\t\t\tif err != nil {\n\t\t\t\tpanic(err)\n\t\t\t}\n\t\t\tfmt.Printf(\"%s\\n\", b)\n\t\tdefault:\n\t\t\tfmt.Println(\"Author: \", a)\n\t\t\tfmt.Println(\"Quote: \", q)\n\t\t}\n\t},\n}\n\nfunc mustString(fs *pflag.FlagSet, name string) string {\n\tv, err := fs.GetString(name)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn v\n}\n\nfunc mustBool(fs *pflag.FlagSet, name string) bool {\n\tv, err := fs.GetBool(name)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn v\n}\n\nfunc init() {\n\trootCmd.AddCommand(getCmd)\n\n\tgetCmd.Flags().BoolP(\"dev\", \"d\", false, \"Uses the dev server instead of prod\")\n\tgetCmd.Flags().String(\"addr\", \"127.0.0.1:80\", \"Set the QOTD server to use, defaults to production\")\n\tgetCmd.Flags().StringP(\"author\", \"a\", \"\", \"Specify the specific author to get a quote for\")\n\tgetCmd.Flags().Bool(\"json\", false, \"Output is in JSON format\")\n}\n"
  },
  {
    "path": "chapter/7/cobra/app/cmd/root.go",
    "content": "/*\nCopyright © 2021 John Doak\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\nall copies 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\nTHE SOFTWARE.\n*/\npackage cmd\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/spf13/cobra\"\n\t\"os\"\n\n\t\"github.com/spf13/viper\"\n)\n\nvar cfgFile string\n\n// rootCmd represents the base command when called without any subcommands\nvar rootCmd = &cobra.Command{\n\tUse:   \"app\",\n\tShort: \"A brief description of your application\",\n\tLong: `A longer description that spans multiple lines and likely contains\nexamples and usage of using your application. For example:\n\nCobra is a CLI library for Go that empowers applications.\nThis application is a tool to generate the needed files\nto quickly create a Cobra application.`,\n\t// Uncomment the following line if your bare application\n\t// has an action associated with it:\n\t// Run: func(cmd *cobra.Command, args []string) { },\n}\n\n// Execute adds all child commands to the root command and sets flags appropriately.\n// This is called by main.main(). It only needs to happen once to the rootCmd.\nfunc Execute(ctx context.Context) {\n\tcobra.CheckErr(rootCmd.ExecuteContext(ctx))\n}\n\nfunc init() {\n\tcobra.OnInitialize(initConfig)\n\n\t// Here you will define your flags and configuration settings.\n\t// Cobra supports persistent flags, which, if defined here,\n\t// will be global for your application.\n\n\trootCmd.PersistentFlags().StringVar(&cfgFile, \"config\", \"\", \"config file (default is $HOME/.app.yaml)\")\n\n\t// Cobra also supports local flags, which will only run\n\t// when this action is called directly.\n\trootCmd.Flags().BoolP(\"toggle\", \"t\", false, \"Help message for toggle\")\n}\n\n// initConfig reads in config file and ENV variables if set.\nfunc initConfig() {\n\tif cfgFile != \"\" {\n\t\t// Use config file from the flag.\n\t\tviper.SetConfigFile(cfgFile)\n\t} else {\n\t\t// Find home directory.\n\t\thome, err := os.UserHomeDir()\n\t\tcobra.CheckErr(err)\n\n\t\t// Search config in home directory with name \".app\" (without extension).\n\t\tviper.AddConfigPath(home)\n\t\tviper.SetConfigType(\"yaml\")\n\t\tviper.SetConfigName(\".app\")\n\t}\n\n\tviper.AutomaticEnv() // read in environment variables that match\n\n\t// If a config file is found, read it in.\n\tif err := viper.ReadInConfig(); err == nil {\n\t\tfmt.Fprintln(os.Stderr, \"Using config file:\", viper.ConfigFileUsed())\n\t}\n}\n"
  },
  {
    "path": "chapter/7/cobra/app/main.go",
    "content": "/*\nCopyright © 2021 John Doak\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\nall copies 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\nTHE SOFTWARE.\n*/\npackage main\n\nimport (\n\t\"context\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/7/cobra/app/cmd\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\tcmd.Execute(ctx)\n}\n"
  },
  {
    "path": "chapter/7/filter_errors/main.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"regexp\"\n)\n\nvar errRE = regexp.MustCompile(`(?i)error`)\n\nfunc main() {\n\tvar s *bufio.Scanner\n\tswitch len(os.Args) {\n\tcase 1:\n\t\tlog.Println(\"No file specified, using stdin\")\n\t\ts = bufio.NewScanner(os.Stdin)\n\tcase 2:\n\t\tf, err := os.Open(os.Args[1])\n\t\tif err != nil {\n\t\t\tlog.Println(err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\ts = bufio.NewScanner(f)\n\tdefault:\n\t\tlog.Println(\"too many arguments provided\")\n\t\tos.Exit(1)\n\t}\n\n\tfor s.Scan() {\n\t\tline := s.Bytes()\n\t\tif errRE.Match(line) {\n\t\t\tfmt.Printf(\"%s\\n\", line)\n\t\t}\n\t}\n\tif err := s.Err(); err != nil {\n\t\tlog.Println(\"Error: \", err)\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "chapter/7/signals/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"os/signal\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"sync\"\n\t\"syscall\"\n\t\"time\"\n)\n\nfunc main() {\n\ttmpFiles, err := os.MkdirTemp(\"\", \"myApp_*\")\n\tif err != nil {\n\t\tlog.Println(\"error creating temp file directory: \", err)\n\t\tos.Exit(1)\n\t}\n\tfmt.Println(\"temp files located at: \", tmpFiles)\n\n\tctx, cancel := context.WithCancel(context.Background())\n\twg := &sync.WaitGroup{}\n\twg.Add(1)\n\n\tsigHandler, stopSig := newSignaling()\n\tsigHandler.register(\n\t\tfunc() {\n\t\t\tcleanup(cancel, wg, tmpFiles)\n\t\t\tos.Exit(1)\n\t\t},\n\t\tsyscall.SIGINT, syscall.SIGTERM,\n\t)\n\tsigHandler.register(\n\t\tfunc() {\n\t\t\tcleanup(cancel, wg, tmpFiles)\n\t\t\tpanic(\"SIGQUIT called\")\n\t\t},\n\t\tsyscall.SIGQUIT,\n\t)\n\n\tgo func() {\n\t\tdefer stopSig()\n\t\tdefer wg.Done()\n\t\tcreateFiles(ctx, tmpFiles)\n\t}()\n\n\tsigHandler.handle()\n\n\tfmt.Println(\"Done\")\n}\n\nfunc createFiles(ctx context.Context, tmpFiles string) {\n\tfor i := 0; i < 30; i++ {\n\t\tif err := ctx.Err(); err != nil {\n\t\t\treturn\n\t\t}\n\t\t_, err := os.Create(filepath.Join(tmpFiles, strconv.Itoa(i)))\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tfmt.Println(\"Created file: \", i)\n\t\ttime.Sleep(1 * time.Second)\n\t}\n}\n\ntype signaling struct {\n\tctx    context.Context\n\tnotify chan os.Signal\n\n\thandlers map[os.Signal]func()\n}\n\nfunc newSignaling() (sig signaling, stop context.CancelFunc) {\n\tctx, cancel := context.WithCancel(context.Background())\n\treturn signaling{\n\t\tctx:      ctx,\n\t\tnotify:   make(chan os.Signal, 1),\n\t\thandlers: map[os.Signal]func(){},\n\t}, cancel\n}\n\nfunc (s signaling) register(f func(), sigs ...os.Signal) {\n\tsignal.Notify(\n\t\ts.notify,\n\t\tsigs...,\n\t)\n\tfor _, sig := range sigs {\n\t\ts.handlers[sig] = f\n\t}\n}\n\nfunc (s signaling) handle() {\n\tfor {\n\t\tselect {\n\t\tcase <-s.ctx.Done():\n\t\t\treturn\n\t\tcase sig := <-s.notify:\n\t\t\tf, ok := s.handlers[sig]\n\t\t\tif !ok {\n\t\t\t\tlog.Printf(\"unknown signal received: %v\", sig)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tf()\n\t\t}\n\t}\n}\n\nfunc cleanup(cancel context.CancelFunc, wg *sync.WaitGroup, tmpFiles string) {\n\tcancel()\n\twg.Wait()\n\n\tif err := os.RemoveAll(tmpFiles); err != nil {\n\t\tfmt.Println(\"problem doing file cleanup: \", err)\n\t\treturn\n\t}\n\tfmt.Println(\"cleanup done\")\n}\n"
  },
  {
    "path": "chapter/8/agent/README.md",
    "content": "# Agent - A System Agent Example\n\n## Introduction\n\nThis is a an example of a System Agent from the \"Go For DevOps\" book by John Doak, David Justice and Sarah Murphy.\n\nThe agent defined here, which can be run by compiling and running the `agent/agent.go`program runs a system agent that exports system stats on port 8081 and runs a gRPC service that allows installing or removing software packages contained in a \".zip\" file. \n\nThese software packages are run in a container and setup on systemd within the user space that the agent runs on (it does not setup system level services).\n\n## Running the agent\n\nYou can run the agent by compiling and deploying the \"agent.go\" file on a Linux box and then starting it. This agent is currently only Linux compatible.\n\n## Running a client\n\nThere is a Cobra client located in `agent/client/cli` that you can compile and run from any device (saying that you compile it for the target platform). \n\nThe Cobra client leverages a Go client at `agent/client` that can be used to programically access an endpoint (or set of endpoints to deploy on multiple machines at once).\n\nWe have included a sample application for you to install and run on the remote side, located in `agent/cli/sample/helloweb.zip`. \n\n## BEWARE\n\nWhile this code runs and has been tested on an Ubuntu instance running on Azure, milleage may vary depending on systemd version and Linux distro.\n\nIn addition, this has not been setup in a production quality way.  For example, you will notice a lack of unit tests and integration tests. We have also not vetted all the containerized security parameters with Linux Kernel security experts. \n\nFinally, this misses features that should be in a production quality version, such as cgroup integration, passing of capabilities and forced network bindings.  \n\nThis is for an example only!"
  },
  {
    "path": "chapter/8/agent/agent.go",
    "content": "/*\nThe agent application is run on a remote system under a user you will connect to via RPC\nand issue commands.\n\nThe applications installed will containerized within that user's space. Even though they\nare in a container, it is best to use a non-priviledged user.\n\nThis also exports sytsem stats on port :8081. There is no security on this web export,\njust an FYI if this system is exposed directly to the internet.\n*/\npackage main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/8/agent/internal/service\"\n)\n\nfunc main() {\n\tagent, err := service.New()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tgo func() {\n\t\terr := http.ListenAndServe(\":8081\", nil)\n\t\tpanic(err)\n\t}()\n\n\tlog.Println(\"Service starting...\")\n\tif err := agent.Start(); err != nil {\n\t\tpanic(err)\n\t}\n}\n"
  },
  {
    "path": "chapter/8/agent/bin/README.md",
    "content": "Contains agent binaries for platforms/architectures.\n\nRunning build.sh from this directory will build all current binaries.\n"
  },
  {
    "path": "chapter/8/agent/bin/build.sh",
    "content": "#!/bin/sh\n\ncd ..\nenv GOOS=linux GOARCH=amd64 go build -o bin/linux_amd64/agent\n"
  },
  {
    "path": "chapter/8/agent/buf.work",
    "content": "version: v1\ndirectories:\n  - proto\n"
  },
  {
    "path": "chapter/8/agent/client/cli/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright © 2021 John Doak\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\nall copies 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\nTHE SOFTWARE.\n"
  },
  {
    "path": "chapter/8/agent/client/cli/cmd/auth.go",
    "content": "package cmd\n\nimport (\n\t\"net\"\n\t\"os\"\n\n\t\"golang.org/x/crypto/ssh\"\n\t\"golang.org/x/crypto/ssh/agent\"\n)\n\nfunc agentAuth() (ssh.AuthMethod, error) {\n\tconn, err := net.Dial(\"unix\", os.Getenv(\"SSH_AUTH_SOCK\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tclient := agent.NewClient(conn)\n\treturn ssh.PublicKeysCallback(client.Signers), nil\n}\n\nfunc publicKey(privateKeyFile string) (ssh.AuthMethod, error) {\n\tk, err := os.ReadFile(privateKeyFile)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsigner, err := ssh.ParsePrivateKey(k)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn ssh.PublicKeys(signer), nil\n}\n\nfunc getAuthFromFlags() (ssh.AuthMethod, error) {\n\tif keyFile != \"\" {\n\t\treturn publicKey(keyFile)\n\t}\n\treturn agentAuth()\n}\n"
  },
  {
    "path": "chapter/8/agent/client/cli/cmd/install.go",
    "content": "/*\nCopyright © 2021 John Doak\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\nall copies 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\nTHE SOFTWARE.\n*/\npackage cmd\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/8/agent/client\"\n\n\t\"github.com/spf13/cobra\"\n\t\"golang.org/x/crypto/ssh\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/8/agent/proto\"\n)\n\n// installCmd represents the install command\nvar installCmd = &cobra.Command{\n\tUse:   \"install [remote endpoint] [package name] [package local path(.zip file] [binary to start]\",\n\tShort: \"Installs an application in a remote container and starts it\",\n\tLong: `Install connects to the system agents giving a package name, a package file, and a binary\nwithin that package to run. It will connect to the system, unpack the contents into a Linux container and\nexecute the binary using systemd. \n\nAn usage example:\n\tcli install 22.47.60.3:22 helloworld ./apps/packages/helloworld.zip helloworld\n`,\n\tRun: func(cmd *cobra.Command, args []string) {\n\t\tauth, err := getAuthFromFlags()\n\t\tif err != nil {\n\t\t\tlog.Println(\"Error: failed to get SSH authorizaion: \", err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tif !strings.HasSuffix(args[2], \".zip\") {\n\t\t\tlog.Println(\"Error: the package file must end in .zip, got: \", args[2])\n\t\t\tos.Exit(1)\n\t\t}\n\n\t\tb, err := os.ReadFile(args[2])\n\t\tif err != nil {\n\t\t\tlog.Println(\"Error: could not read package file: \", err)\n\t\t\tos.Exit(1)\n\t\t}\n\n\t\tc, err := client.New(\n\t\t\targs[0],\n\t\t\t[]ssh.AuthMethod{auth},\n\t\t)\n\t\tif err != nil {\n\t\t\tlog.Println(\"Error: problem connecting to agent: \", err)\n\t\t\tos.Exit(1)\n\t\t}\n\n\t\t_, err = c.Install(\n\t\t\tcontext.Background(),\n\t\t\t&pb.InstallReq{\n\t\t\t\tName:    args[1],\n\t\t\t\tPackage: b,\n\t\t\t\tBinary:  args[3],\n\t\t\t},\n\t\t)\n\t\tif err != nil {\n\t\t\tlog.Println(\"Error: \", err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tfmt.Println(\"Done\")\n\t},\n}\n\nfunc init() {\n\trootCmd.AddCommand(installCmd)\n\n\t// Here you will define your flags and configuration settings.\n\n\t// Cobra supports Persistent Flags which will work for this command\n\t// and all subcommands, e.g.:\n\t// installCmd.PersistentFlags().String(\"foo\", \"\", \"A help for foo\")\n\n\t// Cobra supports local flags which will only run when this command\n\t// is called directly, e.g.:\n\t// installCmd.Flags().BoolP(\"toggle\", \"t\", false, \"Help message for toggle\")\n}\n"
  },
  {
    "path": "chapter/8/agent/client/cli/cmd/remove.go",
    "content": "/*\nCopyright © 2021 John Doak\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\nall copies 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\nTHE SOFTWARE.\n*/\npackage cmd\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/8/agent/client\"\n\n\t\"github.com/spf13/cobra\"\n\t\"golang.org/x/crypto/ssh\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/8/agent/proto\"\n)\n\n// removeCmd represents the remove command\nvar removeCmd = &cobra.Command{\n\tUse:   \"remove [remote endpoint] [package name]\",\n\tShort: \"Remove an application installed by the system agent.\",\n\tLong: `Remove an application installed by the system agent.\"\nAn usage example:\n\ncli remove 22.47.60.3:22 helloworld\n`,\n\tRun: func(cmd *cobra.Command, args []string) {\n\t\tauth, err := getAuthFromFlags()\n\t\tif err != nil {\n\t\t\tlog.Println(\"Error: failed to get SSH authorizaion: \", err)\n\t\t\tos.Exit(1)\n\t\t}\n\n\t\tc, err := client.New(\n\t\t\targs[0],\n\t\t\t[]ssh.AuthMethod{auth},\n\t\t)\n\n\t\t_, err = c.Remove(\n\t\t\tcontext.Background(),\n\t\t\t&pb.RemoveReq{\n\t\t\t\tName: args[1],\n\t\t\t},\n\t\t)\n\t\tif err != nil {\n\t\t\tlog.Println(\"Error: \", err)\n\t\t\tos.Exit(1)\n\t\t}\n\t},\n}\n\nfunc init() {\n\trootCmd.AddCommand(removeCmd)\n\n\t// Here you will define your flags and configuration settings.\n\n\t// Cobra supports Persistent Flags which will work for this command\n\t// and all subcommands, e.g.:\n\t// removeCmd.PersistentFlags().String(\"foo\", \"\", \"A help for foo\")\n\n\t// Cobra supports local flags which will only run when this command\n\t// is called directly, e.g.:\n\t// removeCmd.Flags().BoolP(\"toggle\", \"t\", false, \"Help message for toggle\")\n}\n"
  },
  {
    "path": "chapter/8/agent/client/cli/cmd/root.go",
    "content": "/*\nCopyright © 2021 John Doak\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\nall copies 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\nTHE SOFTWARE.\n*/\npackage cmd\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/spf13/viper\"\n)\n\nvar (\n\tcfgFile  string\n\tendpoint string\n\tkeyFile  string\n)\n\n// rootCmd represents the base command when called without any subcommands\nvar rootCmd = &cobra.Command{\n\tUse:   \"cli\",\n\tShort: \"This application is used to interact with a remote system agent\",\n\tLong: `A longer description that spans multiple lines and likely contains\nexamples and usage of using your application. For example:\n\nCobra is a CLI library for Go that empowers applications.\nThis application is a tool to generate the needed files\nto quickly create a Cobra application.`,\n\t// Uncomment the following line if your bare application\n\t// has an action associated with it:\n\t// Run: func(cmd *cobra.Command, args []string) { },\n}\n\n// Execute adds all child commands to the root command and sets flags appropriately.\n// This is called by main.main(). It only needs to happen once to the rootCmd.\nfunc Execute() {\n\tcobra.CheckErr(rootCmd.Execute())\n}\n\nfunc init() {\n\tcobra.OnInitialize(initConfig)\n\n\t// Here you will define your flags and configuration settings.\n\t// Cobra supports persistent flags, which, if defined here,\n\t// will be global for your application.\n\n\trootCmd.PersistentFlags().StringVar(&cfgFile, \"config\", \"\", \"config file (default is $HOME/.cli.yaml)\")\n\trootCmd.PersistentFlags().StringVar(&endpoint, \"endpoint\", \"\", \"the host:port of the remote agent\")\n\trootCmd.PersistentFlags().StringVar(&keyFile, \"key\", \"\", \"a private key file(pem) path that stores the SSH private key to use\")\n\n\t// Cobra also supports local flags, which will only run\n\t// when this action is called directly.\n\trootCmd.Flags().BoolP(\"toggle\", \"t\", false, \"Help message for toggle\")\n}\n\n// initConfig reads in config file and ENV variables if set.\nfunc initConfig() {\n\tif cfgFile != \"\" {\n\t\t// Use config file from the flag.\n\t\tviper.SetConfigFile(cfgFile)\n\t} else {\n\t\t// Find home directory.\n\t\thome, err := os.UserHomeDir()\n\t\tcobra.CheckErr(err)\n\n\t\t// Search config in home directory with name \".cli\" (without extension).\n\t\tviper.AddConfigPath(home)\n\t\tviper.SetConfigType(\"yaml\")\n\t\tviper.SetConfigName(\".cli\")\n\t}\n\n\tviper.AutomaticEnv() // read in environment variables that match\n\n\t// If a config file is found, read it in.\n\tif err := viper.ReadInConfig(); err == nil {\n\t\tfmt.Fprintln(os.Stderr, \"Using config file:\", viper.ConfigFileUsed())\n\t}\n}\n"
  },
  {
    "path": "chapter/8/agent/client/cli/main.go",
    "content": "/*\nCopyright © 2021 John Doak\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\nall copies 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\nTHE SOFTWARE.\n*/\npackage main\n\nimport \"github.com/PacktPublishing/Go-for-DevOps/chapter/8/agent/client/cli/cmd\"\n\nfunc main() {\n\tcmd.Execute()\n}\n"
  },
  {
    "path": "chapter/8/agent/client/cli/sample/helloweb/helloweb.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n)\n\nvar port = flag.Int(\"port\", 8080, \"The port to run on\")\n\nfunc main() {\n\tflag.Parse()\n\n\thttp.HandleFunc(\"/\", func(w http.ResponseWriter, r *http.Request) {\n\t\tfmt.Fprintf(w, \"Hello web\")\n\t})\n\tlog.Fatal(http.ListenAndServe(fmt.Sprintf(\":%d\", *port), nil))\n}\n"
  },
  {
    "path": "chapter/8/agent/client/client.go",
    "content": "/*\nPackage client provides a client to the system agent that uses SSH and unix sockets\nto make the connection.\n\nThe SSH forwarding is based on code from:\nhttps://stackoverflow.com/questions/21417223/simple-ssh-port-forward-in-golang\n*/\npackage client\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t\"github.com/johnsiilver/serveonssh\"\n\t\"golang.org/x/crypto/ssh\"\n\t\"google.golang.org/grpc\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/8/agent/proto\"\n)\n\ntype Client struct {\n\tuser     string\n\tendpoint string\n\tconn     *grpc.ClientConn\n\tclient   pb.AgentClient\n\tp        serveonssh.Proxy\n}\n\n// New creates a new Client that connects to a remote endpoint via SSH and then\n// uses that connection to dial into a domain socket the agent is using. The\n// gRPC client actually uses a domain socket on this side which is then forwarded\n// over SSH. endpoint is the host:port of the remote endpoint.\nfunc New(endpoint string, auth []ssh.AuthMethod) (*Client, error) {\n\tconfig := &ssh.ClientConfig{\n\t\tUser:            os.Getenv(\"USER\"),\n\t\tAuth:            auth,\n\t\tTimeout:         5 * time.Second,\n\t\tHostKeyCallback: ssh.InsecureIgnoreHostKey(),\n\t}\n\n\tremoteSocket := filepath.Join(\"/home\", config.User, \"/sa/socket/sa.sock\")\n\n\tp, err := serveonssh.New(endpoint, remoteSocket, config)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\topts := []grpc.DialOption{\n\t\tgrpc.WithInsecure(),\n\t\tgrpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {\n\t\t\treturn p.Dialer()()\n\t\t}),\n\t}\n\n\tconn, err := grpc.Dial(\"not needed\", opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Client{\n\t\tendpoint: endpoint,\n\t\tconn:     conn,\n\t\tclient:   pb.NewAgentClient(conn),\n\t\tp:        p,\n\t}, nil\n}\n\nfunc (c *Client) Close() error {\n\tc.conn.Close()\n\tc.p.Close()\n\treturn nil\n}\n\nfunc (c *Client) Install(ctx context.Context, req *pb.InstallReq) (*pb.InstallResp, error) {\n\treturn c.client.Install(ctx, req)\n}\n\nfunc (c *Client) Remove(ctx context.Context, req *pb.RemoveReq) (*pb.RemoveResp, error) {\n\treturn c.client.Remove(ctx, req)\n}\n"
  },
  {
    "path": "chapter/8/agent/internal/service/service.go",
    "content": "// Package service contains the Agent that provides control access to the system\n// and system stats.\npackage service\n\nimport (\n\t\"archive/zip\"\n\t\"bytes\"\n\t\"context\"\n\t\"expvar\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\t\"os/user\"\n\t\"path/filepath\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/coreos/go-systemd/v22/dbus\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n\n\tlinuxproc \"github.com/c9s/goprocinfo/linux\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/8/agent/proto\"\n)\n\nconst (\n\t// pkgDir is the directory in the Agent user's home where we are installing and\n\t// running packages. A more secure version would be to have the agent do this\n\t// in individual user directories that match some user on all machines. However\n\t// this is for illustration purposes only.\n\tpkgDir     = \"sa/packages/\"\n\tserviceExt = \".service\"\n)\n\n// Agent provides a system agent service that runs a gRPC service for doing\n// application installs and an HTTP service for relaying stats.\ntype Agent struct {\n\tpb.UnimplementedAgentServer\n\n\tdbus *dbus.Conn\n\tuser string\n\n\t// mu protects locks.\n\tmu    sync.Mutex\n\tlocks map[string]*sync.Mutex\n\n\tcpuData, memData atomic.Value\n}\n\n// New creates a new Agent instance.\nfunc New() (*Agent, error) {\n\tconn, err := dbus.NewUserConnection()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem connecting to systemd: %w\", err)\n\t}\n\tu, err := user.Current()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Agent{\n\t\tdbus:  conn,\n\t\tuser:  u.Username,\n\t\tlocks: map[string]*sync.Mutex{},\n\t}, nil\n}\n\n// Start starts the agent. As the agent is not intended to ever stop, this has\n// no Stop(). This blocks unless there is a problem.\nfunc (a *Agent) Start() error {\n\tvar sockAddr = filepath.Join(\"/home\", a.user, \"/sa/socket/sa.sock\")\n\tif err := os.MkdirAll(filepath.Dir(sockAddr), 0700); err != nil {\n\t\treturn fmt.Errorf(\"could not create socket dir path: %w\", err)\n\t}\n\t// Remove old socket file if it exists.\n\tos.Remove(sockAddr)\n\n\tif err := a.perfLoop(); err != nil {\n\t\treturn err\n\t}\n\n\tl, err := net.Listen(\"unix\", sockAddr)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not connect to socket: %w\", err)\n\t}\n\n\tvar opts []grpc.ServerOption\n\n\tgrpcServer := grpc.NewServer(opts...)\n\tpb.RegisterAgentServer(grpcServer, a)\n\treturn grpcServer.Serve(l)\n}\n\n// Install implements our gRPC Install RPC.\nfunc (a *Agent) Install(ctx context.Context, req *pb.InstallReq) (*pb.InstallResp, error) {\n\tif err := req.Validate(); err != nil {\n\t\treturn nil, status.Error(codes.InvalidArgument, err.Error())\n\t}\n\n\ta.lock(req.Name)\n\tdefer a.unlock(req.Name, false)\n\n\tloc, err := a.unpack(req.Name, req.Package)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := a.migrate(req, loc); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := a.startProgram(ctx, req.Name); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &pb.InstallResp{}, nil\n}\n\nfunc (a *Agent) Remove(ctx context.Context, req *pb.RemoveReq) (*pb.RemoveResp, error) {\n\ta.lock(req.Name)\n\tdefer a.unlock(req.Name, true)\n\n\tif err := a.stopProgram(ctx, req.Name); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := rmUnitFile(a.dbus, a.user, req); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &pb.RemoveResp{}, nil\n}\n\n// lock locks a named mutex.\nfunc (a *Agent) lock(name string) {\n\ta.mu.Lock()\n\tv, ok := a.locks[name]\n\tif !ok {\n\t\tv = &sync.Mutex{}\n\t\ta.locks[name] = v\n\t}\n\ta.mu.Unlock()\n\n\tv.Lock()\n}\n\n// unlock unlocks a named mutex.\nfunc (a *Agent) unlock(name string, del bool) {\n\ta.mu.Lock()\n\tv, ok := a.locks[name]\n\tif !ok {\n\t\treturn\n\t}\n\tif del {\n\t\tdelete(a.locks, name)\n\t}\n\ta.mu.Unlock()\n\tv.Unlock()\n}\n\n// unpack unpacks a zipfile and stores in in a temporary directory that is returned.\nfunc (a *Agent) unpack(name string, zipFile []byte) (string, error) {\n\tdir, err := os.MkdirTemp(\"\", fmt.Sprintf(\"sa_install_%s_*\", name))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tr, err := zip.NewReader(bytes.NewReader(zipFile), int64(len(zipFile)))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\t// Iterate through the files in the archive,\n\t// printing some of their contents.\n\tfor _, f := range r.File {\n\t\tif err := a.writeFile(f, dir); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\treturn dir, nil\n}\n\n// writeFile writes a zip file under the root directory dir.\nfunc (a *Agent) writeFile(z *zip.File, dir string) error {\n\tif z.FileInfo().IsDir() {\n\t\terr := os.Mkdir(\n\t\t\tfilepath.Join(dir, filepath.FromSlash(z.Name)),\n\t\t\tz.Mode(),\n\t\t)\n\t\treturn err\n\t}\n\n\trc, err := z.Open()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not open file %q: %w\", z.Name, err)\n\t}\n\tdefer rc.Close()\n\n\tnf, err := os.OpenFile(\n\t\tfilepath.Join(dir, filepath.FromSlash(z.Name)),\n\t\tos.O_CREATE|os.O_WRONLY,\n\t\tz.Mode(),\n\t)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not open file in temp diretory: %w\", err)\n\t}\n\tdefer nf.Close()\n\n\t_, err = io.Copy(nf, rc)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"file copy error: %w\", err)\n\t}\n\treturn nil\n}\n\n// migrate shuts down any existing job that is running and migrates our files\n// from the temp location to the final location.\nfunc (a *Agent) migrate(req *pb.InstallReq, loc string) error {\n\tunits, err := a.dbus.ListUnitsByNames([]string{req.Name + serviceExt})\n\tif err == nil && units[0].JobId != 0 {\n\t\tresult := make(chan string, 1)\n\t\t_, err := a.dbus.StopUnit(req.Name+serviceExt, \"replace\", result)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"migate could not stop the service: %w\", err)\n\t\t}\n\t\tswitch v := <-result; v {\n\t\tcase \"done\":\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"systemd StopUnit() returned %q\", v)\n\t\t}\n\t\t//a.conn.KillUnit(name, 15)\n\t}\n\tif err := writeUnitFile(a.dbus, a.user, req); err != nil {\n\t\treturn fmt.Errorf(\"could not write the unit file: %w\", err)\n\t}\n\n\tp := filepath.Join(\"/home\", a.user, pkgDir)\n\tif _, err := os.Stat(p); err == nil {\n\t\tos.RemoveAll(p)\n\t}\n\tif err := os.Rename(loc, p); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// startProgram starts our program under systemd.\nfunc (a *Agent) startProgram(ctx context.Context, name string) error {\n\t// EnableUnitFiles(files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error)\n\tresult := make(chan string, 1)\n\tid, err := a.dbus.StartUnit(name+serviceExt, \"replace\", result)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not start the unit: %w\", err)\n\t}\n\tswitch v := <-result; v {\n\tcase \"done\":\n\t\tlog.Printf(\"new service(%s) is done: %v\", name+serviceExt, id)\n\tdefault:\n\t\treturn fmt.Errorf(\"systemd StartUnit() returned %q\", v)\n\t}\n\n\ttime.Sleep(30 * time.Second)\n\tstatuses, err := a.dbus.ListUnitsByNames([]string{name + serviceExt})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not find unit after start: %s\", err)\n\t}\n\tif len(statuses) != 1 {\n\t\treturn fmt.Errorf(\"could not find unit after start\")\n\t}\n\tstatus := statuses[0]\n\tswitch {\n\tcase status.ActiveState != \"active\":\n\t\treturn fmt.Errorf(\"program is not in active state\")\n\tcase status.SubState != \"running\":\n\t\treturn fmt.Errorf(\"program is not in running state\")\n\tcase status.LoadState != \"loaded\":\n\t\treturn fmt.Errorf(\"program is not in loaded state\")\n\t}\n\treturn nil\n}\n\n// stopProgram stops a program under systemd.\nfunc (a *Agent) stopProgram(ctx context.Context, name string) error {\n\tresult := make(chan string, 1)\n\t_, err := a.dbus.StopUnit(name+serviceExt, \"replace\", result)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not stop the service: %w\", err)\n\t}\n\tswitch v := <-result; v {\n\tcase \"done\":\n\tdefault:\n\t\treturn fmt.Errorf(\"systemd StopUnit() returned %q\", v)\n\t}\n\treturn nil\n}\n\n// collectCPU collects our CPU stats and stores them in .cpuData.\nfunc (a *Agent) collectCPU(resolution int32) error {\n\tstat, err := linuxproc.ReadStat(\"/proc/stat\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tv := &pb.CPUPerfs{\n\t\tResolutionSecs: resolution,\n\t\tUnixTimeNano:   time.Now().UnixNano(),\n\t}\n\tfor _, p := range stat.CPUStats {\n\t\tc := &pb.CPUPerf{\n\t\t\tId:     p.Id,\n\t\t\tUser:   int32(p.User),\n\t\t\tSystem: int32(p.System),\n\t\t\tIdle:   int32(p.Idle),\n\t\t\tIoWait: int32(p.IOWait),\n\t\t\tIrq:    int32(p.IRQ),\n\t\t}\n\t\tv.Cpu = append(v.Cpu, c)\n\t}\n\ta.cpuData.Store(v)\n\treturn nil\n}\n\n// collectMem collects our memory stats and stores them in .memData.\nfunc (a *Agent) collectMem(resolution int32) error {\n\tmem, err := linuxproc.ReadMemInfo(\"/proc/meminfo\")\n\tif err != nil {\n\t\treturn err\n\t}\n\ta.memData.Store(\n\t\t&pb.MemPerf{\n\t\t\tResolutionSecs: resolution,\n\t\t\tUnixTimeNano:   time.Now().UnixNano(),\n\t\t\tTotal:          int32(mem.MemTotal),\n\t\t\tFree:           int32(mem.MemFree),\n\t\t\tAvail:          int32(mem.MemAvailable),\n\t\t},\n\t)\n\treturn nil\n}\n\n// perfLoop grabs data every 10 seconds + gather time and stores it.\n// It also does all registration of these variables with expvar.\n// This should only be called once on systemAgent start.\nfunc (a *Agent) perfLoop() error {\n\tconst resolutionSecs = 10\n\n\tif err := a.collectCPU(resolutionSecs); err != nil {\n\t\treturn err\n\t}\n\tif err := a.collectMem(resolutionSecs); err != nil {\n\t\treturn err\n\t}\n\n\texpvar.Publish(\n\t\t\"system-cpu\",\n\t\texpvar.Func(\n\t\t\tfunc() interface{} {\n\t\t\t\treturn a.cpuData.Load().(*pb.CPUPerfs)\n\t\t\t},\n\t\t),\n\t)\n\texpvar.Publish(\n\t\t\"system-mem\",\n\t\texpvar.Func(\n\t\t\tfunc() interface{} {\n\t\t\t\treturn a.memData.Load().(*pb.MemPerf)\n\t\t\t},\n\t\t),\n\t)\n\n\tgo func() {\n\t\tfor {\n\t\t\ttime.Sleep(resolutionSecs * time.Second)\n\t\t\tif err := a.collectCPU(resolutionSecs); err != nil {\n\t\t\t\tlog.Println(err)\n\t\t\t}\n\t\t\tif err := a.collectMem(resolutionSecs); err != nil {\n\t\t\t\tlog.Println(err)\n\t\t\t}\n\t\t}\n\t}()\n\treturn nil\n}\n"
  },
  {
    "path": "chapter/8/agent/internal/service/unit_file.go",
    "content": "package service\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"os/user\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\t\"text/template\"\n\n\t\"github.com/coreos/go-systemd/v22/dbus\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/8/agent/proto\"\n)\n\nvar systemdUnits = \"\"\n\nfunc init() {\n\tu, err := user.Current()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tsystemdUnits = filepath.Join(\"/home\", u.Username, \".config/systemd/user\")\n\tif err := os.MkdirAll(systemdUnits, 0700); err != nil {\n\t\tpanic(err)\n\t}\n}\n\nvar unitTmpl = template.Must(\n\ttemplate.New(\"unit\").Parse(\n\t\t`\n[Unit]\nDescription={{.Desc}}\n\nStartLimitIntervalSec=500\nStartLimitBurst=5\n\n[Service]\nPrivateUsers=true\nPrivateDevices=true\nReadOnlyPaths=/\n\nRootDirectory={{.RootPath}}\nReadWritePaths={{.RootPath}}\n\nSecureBits=noroot\n\nRestart=on-failure\nRestartSec=5s\n\nExecStart={{.BinaryPath}} {{.Args}}\n\n[Install]\nWantedBy=multi-user.target\n`))\n\ntype unitArgs struct {\n\tDesc       string\n\tBinaryPath string\n\tRootPath   string\n\tArgs       string\n}\n\nvar wufMu sync.Mutex\n\nfunc writeUnitFile(dbusConn *dbus.Conn, user string, req *pb.InstallReq) error {\n\ta := unitArgs{\n\t\tDesc:       req.Name,\n\t\tBinaryPath: filepath.Join(\"/\", req.Binary),\n\t\t//BinaryPath: filepath.Join(\"/home\", user, pkgDir, req.Name, req.Binary),\n\t\tRootPath: filepath.Join(\"/home\", user, pkgDir, req.Name),\n\t\tArgs:     strings.Join(req.Args, \" \"),\n\t}\n\tunit := req.Name + \".service\"\n\n\tp := filepath.Join(systemdUnits, unit)\n\n\tf, err := os.OpenFile(p, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not create systemd unit file: %w\", err)\n\t}\n\n\tif err := unitTmpl.Execute(f, a); err != nil {\n\t\tf.Close()\n\t\treturn err\n\t}\n\tf.Close()\n\n\t// Let's only try to reload the daemon one at a time.\n\twufMu.Lock()\n\tdefer wufMu.Unlock()\n\treturn dbusConn.Reload()\n}\n\nfunc rmUnitFile(dbusConn *dbus.Conn, user string, req *pb.RemoveReq) error {\n\tunit := req.Name + \".service\"\n\n\tp := filepath.Join(systemdUnits, unit)\n\n\tif err := os.Remove(p); err != nil {\n\t\tif errors.Is(err.(*os.PathError), fs.ErrNotExist) {\n\t\t\treturn nil\n\t\t}\n\t\treturn err\n\t}\n\n\t// Let's only try to reload the daemon one at a time.\n\twufMu.Lock()\n\tdefer wufMu.Unlock()\n\treturn dbusConn.Reload()\n}\n"
  },
  {
    "path": "chapter/8/agent/proto/agent.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.25.0-devel\n// \tprotoc        v3.18.0\n// source: agent.proto\n\npackage agent\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 InstallReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tName    string   `protobuf:\"bytes,1,opt,name=name,proto3\" json:\"name,omitempty\"`\n\tPackage []byte   `protobuf:\"bytes,2,opt,name=package,proto3\" json:\"package,omitempty\"`\n\tBinary  string   `protobuf:\"bytes,3,opt,name=binary,proto3\" json:\"binary,omitempty\"`\n\tArgs    []string `protobuf:\"bytes,4,rep,name=args,proto3\" json:\"args,omitempty\"`\n}\n\nfunc (x *InstallReq) Reset() {\n\t*x = InstallReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_agent_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *InstallReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*InstallReq) ProtoMessage() {}\n\nfunc (x *InstallReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_agent_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 InstallReq.ProtoReflect.Descriptor instead.\nfunc (*InstallReq) Descriptor() ([]byte, []int) {\n\treturn file_agent_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *InstallReq) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\nfunc (x *InstallReq) GetPackage() []byte {\n\tif x != nil {\n\t\treturn x.Package\n\t}\n\treturn nil\n}\n\nfunc (x *InstallReq) GetBinary() string {\n\tif x != nil {\n\t\treturn x.Binary\n\t}\n\treturn \"\"\n}\n\nfunc (x *InstallReq) GetArgs() []string {\n\tif x != nil {\n\t\treturn x.Args\n\t}\n\treturn nil\n}\n\ntype InstallResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *InstallResp) Reset() {\n\t*x = InstallResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_agent_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *InstallResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*InstallResp) ProtoMessage() {}\n\nfunc (x *InstallResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_agent_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 InstallResp.ProtoReflect.Descriptor instead.\nfunc (*InstallResp) Descriptor() ([]byte, []int) {\n\treturn file_agent_proto_rawDescGZIP(), []int{1}\n}\n\ntype RemoveReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tName string `protobuf:\"bytes,1,opt,name=name,proto3\" json:\"name,omitempty\"`\n}\n\nfunc (x *RemoveReq) Reset() {\n\t*x = RemoveReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_agent_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *RemoveReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*RemoveReq) ProtoMessage() {}\n\nfunc (x *RemoveReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_agent_proto_msgTypes[2]\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 RemoveReq.ProtoReflect.Descriptor instead.\nfunc (*RemoveReq) Descriptor() ([]byte, []int) {\n\treturn file_agent_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *RemoveReq) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\ntype RemoveResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *RemoveResp) Reset() {\n\t*x = RemoveResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_agent_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *RemoveResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*RemoveResp) ProtoMessage() {}\n\nfunc (x *RemoveResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_agent_proto_msgTypes[3]\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 RemoveResp.ProtoReflect.Descriptor instead.\nfunc (*RemoveResp) Descriptor() ([]byte, []int) {\n\treturn file_agent_proto_rawDescGZIP(), []int{3}\n}\n\ntype CPUPerfs struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tResolutionSecs int32      `protobuf:\"varint,1,opt,name=resolutionSecs,proto3\" json:\"resolutionSecs,omitempty\"`\n\tUnixTimeNano   int64      `protobuf:\"varint,2,opt,name=unix_time_nano,json=unixTimeNano,proto3\" json:\"unix_time_nano,omitempty\"`\n\tCpu            []*CPUPerf `protobuf:\"bytes,3,rep,name=cpu,proto3\" json:\"cpu,omitempty\"`\n}\n\nfunc (x *CPUPerfs) Reset() {\n\t*x = CPUPerfs{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_agent_proto_msgTypes[4]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *CPUPerfs) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*CPUPerfs) ProtoMessage() {}\n\nfunc (x *CPUPerfs) ProtoReflect() protoreflect.Message {\n\tmi := &file_agent_proto_msgTypes[4]\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 CPUPerfs.ProtoReflect.Descriptor instead.\nfunc (*CPUPerfs) Descriptor() ([]byte, []int) {\n\treturn file_agent_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (x *CPUPerfs) GetResolutionSecs() int32 {\n\tif x != nil {\n\t\treturn x.ResolutionSecs\n\t}\n\treturn 0\n}\n\nfunc (x *CPUPerfs) GetUnixTimeNano() int64 {\n\tif x != nil {\n\t\treturn x.UnixTimeNano\n\t}\n\treturn 0\n}\n\nfunc (x *CPUPerfs) GetCpu() []*CPUPerf {\n\tif x != nil {\n\t\treturn x.Cpu\n\t}\n\treturn nil\n}\n\ntype CPUPerf struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tId     string `protobuf:\"bytes,1,opt,name=id,proto3\" json:\"id,omitempty\"`\n\tUser   int32  `protobuf:\"varint,2,opt,name=user,proto3\" json:\"user,omitempty\"`\n\tSystem int32  `protobuf:\"varint,3,opt,name=system,proto3\" json:\"system,omitempty\"`\n\tIdle   int32  `protobuf:\"varint,4,opt,name=idle,proto3\" json:\"idle,omitempty\"`\n\tIoWait int32  `protobuf:\"varint,5,opt,name=io_wait,json=ioWait,proto3\" json:\"io_wait,omitempty\"`\n\tIrq    int32  `protobuf:\"varint,6,opt,name=irq,proto3\" json:\"irq,omitempty\"`\n}\n\nfunc (x *CPUPerf) Reset() {\n\t*x = CPUPerf{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_agent_proto_msgTypes[5]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *CPUPerf) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*CPUPerf) ProtoMessage() {}\n\nfunc (x *CPUPerf) ProtoReflect() protoreflect.Message {\n\tmi := &file_agent_proto_msgTypes[5]\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 CPUPerf.ProtoReflect.Descriptor instead.\nfunc (*CPUPerf) Descriptor() ([]byte, []int) {\n\treturn file_agent_proto_rawDescGZIP(), []int{5}\n}\n\nfunc (x *CPUPerf) GetId() string {\n\tif x != nil {\n\t\treturn x.Id\n\t}\n\treturn \"\"\n}\n\nfunc (x *CPUPerf) GetUser() int32 {\n\tif x != nil {\n\t\treturn x.User\n\t}\n\treturn 0\n}\n\nfunc (x *CPUPerf) GetSystem() int32 {\n\tif x != nil {\n\t\treturn x.System\n\t}\n\treturn 0\n}\n\nfunc (x *CPUPerf) GetIdle() int32 {\n\tif x != nil {\n\t\treturn x.Idle\n\t}\n\treturn 0\n}\n\nfunc (x *CPUPerf) GetIoWait() int32 {\n\tif x != nil {\n\t\treturn x.IoWait\n\t}\n\treturn 0\n}\n\nfunc (x *CPUPerf) GetIrq() int32 {\n\tif x != nil {\n\t\treturn x.Irq\n\t}\n\treturn 0\n}\n\ntype MemPerf struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tResolutionSecs int32 `protobuf:\"varint,1,opt,name=resolutionSecs,proto3\" json:\"resolutionSecs,omitempty\"`\n\tUnixTimeNano   int64 `protobuf:\"varint,2,opt,name=unix_time_nano,json=unixTimeNano,proto3\" json:\"unix_time_nano,omitempty\"`\n\tTotal          int32 `protobuf:\"varint,3,opt,name=total,proto3\" json:\"total,omitempty\"`\n\tFree           int32 `protobuf:\"varint,4,opt,name=free,proto3\" json:\"free,omitempty\"`\n\tAvail          int32 `protobuf:\"varint,5,opt,name=avail,proto3\" json:\"avail,omitempty\"`\n}\n\nfunc (x *MemPerf) Reset() {\n\t*x = MemPerf{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_agent_proto_msgTypes[6]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *MemPerf) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*MemPerf) ProtoMessage() {}\n\nfunc (x *MemPerf) ProtoReflect() protoreflect.Message {\n\tmi := &file_agent_proto_msgTypes[6]\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 MemPerf.ProtoReflect.Descriptor instead.\nfunc (*MemPerf) Descriptor() ([]byte, []int) {\n\treturn file_agent_proto_rawDescGZIP(), []int{6}\n}\n\nfunc (x *MemPerf) GetResolutionSecs() int32 {\n\tif x != nil {\n\t\treturn x.ResolutionSecs\n\t}\n\treturn 0\n}\n\nfunc (x *MemPerf) GetUnixTimeNano() int64 {\n\tif x != nil {\n\t\treturn x.UnixTimeNano\n\t}\n\treturn 0\n}\n\nfunc (x *MemPerf) GetTotal() int32 {\n\tif x != nil {\n\t\treturn x.Total\n\t}\n\treturn 0\n}\n\nfunc (x *MemPerf) GetFree() int32 {\n\tif x != nil {\n\t\treturn x.Free\n\t}\n\treturn 0\n}\n\nfunc (x *MemPerf) GetAvail() int32 {\n\tif x != nil {\n\t\treturn x.Avail\n\t}\n\treturn 0\n}\n\nvar File_agent_proto protoreflect.FileDescriptor\n\nvar file_agent_proto_rawDesc = []byte{\n\t0x0a, 0x0b, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x73,\n\t0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x22, 0x66, 0x0a, 0x0a, 0x49,\n\t0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,\n\t0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a,\n\t0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07,\n\t0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x69, 0x6e, 0x61, 0x72,\n\t0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x12,\n\t0x12, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x61,\n\t0x72, 0x67, 0x73, 0x22, 0x0d, 0x0a, 0x0b, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x52, 0x65,\n\t0x73, 0x70, 0x22, 0x1f, 0x0a, 0x09, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71, 0x12,\n\t0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,\n\t0x61, 0x6d, 0x65, 0x22, 0x0c, 0x0a, 0x0a, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x73,\n\t0x70, 0x22, 0x81, 0x01, 0x0a, 0x08, 0x43, 0x50, 0x55, 0x50, 0x65, 0x72, 0x66, 0x73, 0x12, 0x26,\n\t0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x63, 0x73,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69,\n\t0x6f, 0x6e, 0x53, 0x65, 0x63, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x74,\n\t0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c,\n\t0x75, 0x6e, 0x69, 0x78, 0x54, 0x69, 0x6d, 0x65, 0x4e, 0x61, 0x6e, 0x6f, 0x12, 0x27, 0x0a, 0x03,\n\t0x63, 0x70, 0x75, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x73, 0x79, 0x73, 0x74,\n\t0x65, 0x6d, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x50, 0x55, 0x50, 0x65, 0x72, 0x66,\n\t0x52, 0x03, 0x63, 0x70, 0x75, 0x22, 0x84, 0x01, 0x0a, 0x07, 0x43, 0x50, 0x55, 0x50, 0x65, 0x72,\n\t0x66, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69,\n\t0x64, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52,\n\t0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18,\n\t0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x12, 0x0a,\n\t0x04, 0x69, 0x64, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x69, 0x64, 0x6c,\n\t0x65, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x6f, 0x5f, 0x77, 0x61, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01,\n\t0x28, 0x05, 0x52, 0x06, 0x69, 0x6f, 0x57, 0x61, 0x69, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x72,\n\t0x71, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x69, 0x72, 0x71, 0x22, 0x97, 0x01, 0x0a,\n\t0x07, 0x4d, 0x65, 0x6d, 0x50, 0x65, 0x72, 0x66, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f,\n\t0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x63, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,\n\t0x52, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x63, 0x73,\n\t0x12, 0x24, 0x0a, 0x0e, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x61,\n\t0x6e, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x75, 0x6e, 0x69, 0x78, 0x54, 0x69,\n\t0x6d, 0x65, 0x4e, 0x61, 0x6e, 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18,\n\t0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x12, 0x0a, 0x04,\n\t0x66, 0x72, 0x65, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x66, 0x72, 0x65, 0x65,\n\t0x12, 0x14, 0x0a, 0x05, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52,\n\t0x05, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x32, 0x88, 0x01, 0x0a, 0x05, 0x41, 0x67, 0x65, 0x6e, 0x74,\n\t0x12, 0x40, 0x0a, 0x07, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x12, 0x18, 0x2e, 0x73, 0x79,\n\t0x73, 0x74, 0x65, 0x6d, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61,\n\t0x6c, 0x6c, 0x52, 0x65, 0x71, 0x1a, 0x19, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x61,\n\t0x67, 0x65, 0x6e, 0x74, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70,\n\t0x22, 0x00, 0x12, 0x3d, 0x0a, 0x06, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x12, 0x17, 0x2e, 0x73,\n\t0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f,\n\t0x76, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x61,\n\t0x67, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x22,\n\t0x00, 0x42, 0x50, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,\n\t0x50, 0x61, 0x63, 0x6b, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x2f,\n\t0x47, 0x6f, 0x2d, 0x66, 0x6f, 0x72, 0x2d, 0x44, 0x65, 0x76, 0x4f, 0x70, 0x73, 0x2f, 0x63, 0x68,\n\t0x61, 0x70, 0x74, 0x65, 0x72, 0x2f, 0x31, 0x2f, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2f,\n\t0x36, 0x2f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x61, 0x67,\n\t0x65, 0x6e, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_agent_proto_rawDescOnce sync.Once\n\tfile_agent_proto_rawDescData = file_agent_proto_rawDesc\n)\n\nfunc file_agent_proto_rawDescGZIP() []byte {\n\tfile_agent_proto_rawDescOnce.Do(func() {\n\t\tfile_agent_proto_rawDescData = protoimpl.X.CompressGZIP(file_agent_proto_rawDescData)\n\t})\n\treturn file_agent_proto_rawDescData\n}\n\nvar file_agent_proto_msgTypes = make([]protoimpl.MessageInfo, 7)\nvar file_agent_proto_goTypes = []interface{}{\n\t(*InstallReq)(nil),  // 0: system.agent.InstallReq\n\t(*InstallResp)(nil), // 1: system.agent.InstallResp\n\t(*RemoveReq)(nil),   // 2: system.agent.RemoveReq\n\t(*RemoveResp)(nil),  // 3: system.agent.RemoveResp\n\t(*CPUPerfs)(nil),    // 4: system.agent.CPUPerfs\n\t(*CPUPerf)(nil),     // 5: system.agent.CPUPerf\n\t(*MemPerf)(nil),     // 6: system.agent.MemPerf\n}\nvar file_agent_proto_depIdxs = []int32{\n\t5, // 0: system.agent.CPUPerfs.cpu:type_name -> system.agent.CPUPerf\n\t0, // 1: system.agent.Agent.Install:input_type -> system.agent.InstallReq\n\t2, // 2: system.agent.Agent.Remove:input_type -> system.agent.RemoveReq\n\t1, // 3: system.agent.Agent.Install:output_type -> system.agent.InstallResp\n\t3, // 4: system.agent.Agent.Remove:output_type -> system.agent.RemoveResp\n\t3, // [3:5] is the sub-list for method output_type\n\t1, // [1:3] is the sub-list for method input_type\n\t1, // [1:1] is the sub-list for extension type_name\n\t1, // [1:1] is the sub-list for extension extendee\n\t0, // [0:1] is the sub-list for field type_name\n}\n\nfunc init() { file_agent_proto_init() }\nfunc file_agent_proto_init() {\n\tif File_agent_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_agent_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*InstallReq); 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_agent_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*InstallResp); 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_agent_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*RemoveReq); 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_agent_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*RemoveResp); 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_agent_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*CPUPerfs); 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_agent_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*CPUPerf); 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_agent_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*MemPerf); 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_agent_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   7,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_agent_proto_goTypes,\n\t\tDependencyIndexes: file_agent_proto_depIdxs,\n\t\tMessageInfos:      file_agent_proto_msgTypes,\n\t}.Build()\n\tFile_agent_proto = out.File\n\tfile_agent_proto_rawDesc = nil\n\tfile_agent_proto_goTypes = nil\n\tfile_agent_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "chapter/8/agent/proto/agent.proto",
    "content": "syntax = \"proto3\";\npackage system.agent;\n\noption go_package = \"github.com/PacktPublishing/Go-for-DevOps/chapter/6/agent/proto/agent\";\n\nmessage InstallReq {\n\tstring name = 1;\n\tbytes package = 2;\n\tstring binary = 3;\n\trepeated string args = 4;\n}\n\nmessage InstallResp {}\n\nmessage RemoveReq {\n\tstring name = 1;\n}\n\nmessage RemoveResp {}\n\nmessage CPUPerfs {\n\tint32 resolutionSecs = 1;\n\tint64 unix_time_nano = 2;\n\trepeated CPUPerf cpu = 3;\n}\n\nmessage CPUPerf {\n\tstring id = 1;\n\tint32 user = 2;\n\tint32 system = 3;\n\tint32 idle = 4;\n\tint32 io_wait = 5;\n\tint32 irq = 6;\n}\n\nmessage MemPerf {\n\tint32 resolutionSecs = 1;\n\tint64 unix_time_nano = 2;\n\tint32 total = 3;\n\tint32 free = 4;\n\tint32 avail = 5;\n}\n\nservice Agent {\n   rpc Install(InstallReq) returns (InstallResp) {};\n   rpc Remove(RemoveReq) returns (RemoveResp) {};\n}\n"
  },
  {
    "path": "chapter/8/agent/proto/agent_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n\npackage agent\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// AgentClient is the client API for Agent 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 AgentClient interface {\n\tInstall(ctx context.Context, in *InstallReq, opts ...grpc.CallOption) (*InstallResp, error)\n\tRemove(ctx context.Context, in *RemoveReq, opts ...grpc.CallOption) (*RemoveResp, error)\n}\n\ntype agentClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewAgentClient(cc grpc.ClientConnInterface) AgentClient {\n\treturn &agentClient{cc}\n}\n\nfunc (c *agentClient) Install(ctx context.Context, in *InstallReq, opts ...grpc.CallOption) (*InstallResp, error) {\n\tout := new(InstallResp)\n\terr := c.cc.Invoke(ctx, \"/system.agent.Agent/Install\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *agentClient) Remove(ctx context.Context, in *RemoveReq, opts ...grpc.CallOption) (*RemoveResp, error) {\n\tout := new(RemoveResp)\n\terr := c.cc.Invoke(ctx, \"/system.agent.Agent/Remove\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// AgentServer is the server API for Agent service.\n// All implementations must embed UnimplementedAgentServer\n// for forward compatibility\ntype AgentServer interface {\n\tInstall(context.Context, *InstallReq) (*InstallResp, error)\n\tRemove(context.Context, *RemoveReq) (*RemoveResp, error)\n\tmustEmbedUnimplementedAgentServer()\n}\n\n// UnimplementedAgentServer must be embedded to have forward compatible implementations.\ntype UnimplementedAgentServer struct {\n}\n\nfunc (UnimplementedAgentServer) Install(context.Context, *InstallReq) (*InstallResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method Install not implemented\")\n}\nfunc (UnimplementedAgentServer) Remove(context.Context, *RemoveReq) (*RemoveResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method Remove not implemented\")\n}\nfunc (UnimplementedAgentServer) mustEmbedUnimplementedAgentServer() {}\n\n// UnsafeAgentServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to AgentServer will\n// result in compilation errors.\ntype UnsafeAgentServer interface {\n\tmustEmbedUnimplementedAgentServer()\n}\n\nfunc RegisterAgentServer(s grpc.ServiceRegistrar, srv AgentServer) {\n\ts.RegisterService(&Agent_ServiceDesc, srv)\n}\n\nfunc _Agent_Install_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(InstallReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(AgentServer).Install(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/system.agent.Agent/Install\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(AgentServer).Install(ctx, req.(*InstallReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Agent_Remove_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(RemoveReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(AgentServer).Remove(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/system.agent.Agent/Remove\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(AgentServer).Remove(ctx, req.(*RemoveReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// Agent_ServiceDesc is the grpc.ServiceDesc for Agent 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 Agent_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"system.agent.Agent\",\n\tHandlerType: (*AgentServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"Install\",\n\t\t\tHandler:    _Agent_Install_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"Remove\",\n\t\t\tHandler:    _Agent_Remove_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"agent.proto\",\n}\n"
  },
  {
    "path": "chapter/8/agent/proto/buf.gen.yaml",
    "content": "version: v1\nplugins:\n  - name: go\n    out: ./\n    opt:\n      - paths=source_relative\n  - name: go-grpc\n    out: ./\n    opt:\n      - paths=source_relative\n"
  },
  {
    "path": "chapter/8/agent/proto/buf.yaml",
    "content": "version: v1\nlint:\n  use:\n    - DEFAULT\nbreaking:\n  use:\n    - FILE\n"
  },
  {
    "path": "chapter/8/agent/proto/extra.go",
    "content": "/*\nThis file contains manually added methods for our proto that are useful to the server.\n*/\n\npackage agent\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"google.golang.org/protobuf/encoding/protojson\"\n)\n\n// Validate is used to validate an InstallReq.\nfunc (i *InstallReq) Validate() error {\n\ti.Name = strings.TrimSpace(i.Name)\n\ti.Binary = strings.TrimSpace(i.Binary)\n\tswitch \"\" {\n\tcase i.Name:\n\t\treturn fmt.Errorf(\"Name must be set\")\n\tcase i.Binary:\n\t\treturn fmt.Errorf(\"Binary must be set\")\n\t}\n\tif len(i.Package) == 0 {\n\t\treturn fmt.Errorf(\"Package must be set\")\n\t}\n\tswitch {\n\tcase !validName(i.Name):\n\t\treturn fmt.Errorf(\"Name(%s) must only contain 0-9, A-Z, a-z\", i.Name)\n\tcase !validName(i.Binary):\n\t\treturn fmt.Errorf(\"Binary(%s) must only contain 0-9, A-Z, a-z\", i.Binary)\n\t}\n\treturn nil\n}\n\nfunc validName(s string) bool {\n\tfor i := 0; i < len(s); i++ {\n\t\tswitch {\n\t\t// 0-9\n\t\tcase s[i] >= 48 && s[i] <= 57:\n\t\t// A-Z\n\t\tcase s[i] >= 65 && s[i] <= 90:\n\t\t// a-z\n\t\tcase s[i] >= 97 && s[i] <= 122:\n\t\tdefault:\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// MarshalJSON implement json.Marshaller for CPUPerfs so that we use the\n// protojson.Marshal() instead of the standard marshaller.\nfunc (x *CPUPerfs) MarshalJSON() ([]byte, error) {\n\treturn protojson.Marshal(x)\n}\n\n// UnmarshalJSON implement json.Unmarshaller for CPUPerfs so that we use the\n// protojson.Unmarshal() instead of the standard unmarshaller.\nfunc (x *CPUPerfs) UnmarshalJSON(b []byte) error {\n\treturn protojson.Unmarshal(b, x)\n}\n\n// MarshalJSON implement json.Marshaller for MemPerf so that we use the\n// protojson.Marshal() instead of the standard marshaller.\nfunc (x *MemPerf) MarshalJSON() ([]byte, error) {\n\treturn protojson.Marshal(x)\n}\n\n// UnmarshalJSON implement json.Unmarshaller for MemPerf so that we use the\n// protojson.Unmarshal() instead of the standard unmarshaller.\nfunc (x *MemPerf) UnmarshalJSON(b []byte) error {\n\treturn protojson.Unmarshal(b, x)\n}\n"
  },
  {
    "path": "chapter/8/rollout/README.md",
    "content": "## Rollout Demo\n\n### Setup\n\nFor this demo, you are going to need setup in which you have 1 system acting as a load balancer and other systems that act as backends.  We will be pushing out an update that:\n\n- Removes a system form the load balancer\n- Upgrades the binary on the remote system\n- Tests the binary health\n- Adds the system back to the load balancer\n\nIf no load balancer pool exists, it will add one. If one exists, the pool must be healthy and have the same backends as what we are upgrading according to our configuration file.\n\nHow you setup the test systems is an exercise for the reader. For testing we used Azure VM scale sets for the backends and a single VM for the load balancer. You could do this with local VMs on your workstation, physical machines or whatever means you want.\n\nHere are the prerequisites:\n- The system where the rollout is executed must have access to:\n\t- The system with the load balancer\n\t- All backends\n\t- Must have an SSH key file that can log into each system\n- All systems must be able to communicate to each other on SSH\n- Backends must be able to open port 8082\n- LB must be able to open port 8080 and 8081\n- Write a services.json file that represents our upgrade. Re-write the sample to your settings.\n\n**Note**: Like most example code, this isn't production quality. This is simply meant as an example to teach lessons and show off capabilites you can use for your own nees.\n\n### Running the LB\n\nYou will need to compile and push the rollout/lb binary to the system that will act as your load balancer. Running it is a simple as:\n\n```bash\n./lb\nload balancer started(8080)...\ngrpc server started(8081)...\n```\n\n**Note**: The load balancer listens on all ports and has no security for gRPC. So don't expose this on an addressable IP.\n\n### Prepare the rollout\n\nYou will need to compile the rollout binary and put it on whatever system is going to do the rollout.\n\nIn the same directory you will need to copy the webserver you want to deploy. You can use the one in `rollout/lb/sample` if you want. But whatever binary it is, it must:\n- Answer with something on /\n- Answer with \"ok\" on /healthz\n- Run on port 8082\n\nFinally, you need to have a `services.json` file that represents the rollout. Modify the sample one to your needs. If you have questions on the values, have a look at config.go where all the fields are detailed.\n\n### Doing a rollout\n\nWith these files located in the same directory:\n- rollout\n- services.json\n- webserver\n\nYou can simply do:\n```bash\n./rollout --keyFile=/home/[user]/.ssh/[key].pem services.json\n```\n\nThis should start the process and do a rollout. The output will look like:\n\n```\nSetup LB with pool `/`\nStarting Workflow\nRunning canary on: 10.0.0.5\nSleeping after canary for 1 minutes\nUpgrading endpoint: 10.0.0.7\nUpgrading endpoint: 10.0.0.6\nUpgrading endpoint: 10.0.0.8\nUpgrading endpoint: 10.0.0.9\nWorkflow Completed with no failures\n```\n\n### Common errors\n\n```bash\nWorkflow Failed: esPreconditionFailure\nFailed State  Error                                                                      \nPrecondition  checkLBState precondition fail: expected backends(5) != found backends(1) \n```\nThis usually occurs because you did a failed rollout. When a rollout happens, it expects either there to be an empty load balancer pool or all expected backends to be in and healthy.  You can just restart the \"lb\" binary and run again.\n\n\n"
  },
  {
    "path": "chapter/8/rollout/config.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"golang.org/x/crypto/ssh\"\n)\n\n// config represents the configuration file that details the work to be done.\ntype config struct {\n\t// Concurrency is the number of servers that can be upgraded at a time.\n\tConcurrency int32\n\t// CanaryNum is the number of canaries to do before proceeding with a general rollout.\n\t// Any canary failure fails the workflow. Canaries execute one at a time.\n\tCanaryNum int32\n\t// MaxFailures is the maximum number of failures to tolerate before stopping.\n\t// You can have more failures than MaxFailures due to concurrency settings.\n\tMaxFailures int32\n\t// Src is the path on disk to the binary to push.\n\tSrc string\n\t// Dst is the path on the remote disk to copy the binary to.\n\tDst string\n\t// LB is the host:port of the load balancer.\n\tLB string\n\t// Pattern is the load balancer's Pool pattern.\n\tPattern string\n\t// Backends are the backends that need to be updated, simply the host in IP form.\n\tBackends []string\n\t// BackendUser is the user to use when logging in to the backends.\n\tBackendUser string\n\t// BinaryPort is the port the binary will start on. This is used to configure the\n\t// load balancer.\n\tBinaryPort int\n\n\t// ssh is the SSH client configuration for all host connections. This is not set\n\t// in the config file, it is added in during main().\n\tssh *ssh.ClientConfig\n}\n\n// validate does basic validation of the config.\nfunc (s config) validate() error {\n\tif _, _, err := checkIPPort(s.LB); err != nil {\n\t\treturn fmt.Errorf(\"LB(%s) is not correct: %w\", s.LB, err)\n\t}\n\tif len(s.Backends) < 1 {\n\t\treturn fmt.Errorf(\"must specify some Backends\")\n\t}\n\tfor _, b := range s.Backends {\n\t\t_, err := checkIP(b)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"Backend(%s) is not correct: %w\", b, err)\n\t\t}\n\t}\n\tif s.BinaryPort < 1 {\n\t\treturn fmt.Errorf(\"BinaryPort(%d) is invalid\", s.BinaryPort)\n\t}\n\tif strings.TrimSpace(s.Pattern) == \"\" {\n\t\treturn fmt.Errorf(\"Pattern(%s) is invalid\", s.Pattern)\n\t}\n\tif s.Concurrency < 1 {\n\t\treturn fmt.Errorf(\"Concurrency(%d) is invalid\", s.Concurrency)\n\t}\n\treturn nil\n}\n\nfunc checkIP(s string) (net.IP, error) {\n\tip := net.ParseIP(s)\n\tif ip == nil {\n\t\treturn nil, fmt.Errorf(\"%q is not a valid IP\", s)\n\t}\n\treturn ip, nil\n}\n\n// checkIPPort takes a host:port string and splits it out and verifies it.\nfunc checkIPPort(b string) (net.IP, int32, error) {\n\th, ps, err := net.SplitHostPort(b)\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\tp, _ := strconv.Atoi(ps)\n\tip := net.ParseIP(h)\n\tif ip == nil {\n\t\treturn nil, 0, err\n\t}\n\tif p < 1 || p > 65534 {\n\t\treturn nil, 0, fmt.Errorf(\"invalid port: %d\", p)\n\t}\n\treturn ip, int32(p), nil\n}\n"
  },
  {
    "path": "chapter/8/rollout/endstate_string.go",
    "content": "// Code generated by \"stringer -type=endState\"; DO NOT EDIT.\n\npackage main\n\nimport \"strconv\"\n\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[esUnknown-0]\n\t_ = x[esSuccess-1]\n\t_ = x[esPreconditionFailure-2]\n\t_ = x[esCanaryFailure-3]\n\t_ = x[esMaxFailures-4]\n}\n\nconst _endState_name = \"esUnknownesSuccessesPreconditionFailureesCanaryFailureesMaxFailures\"\n\nvar _endState_index = [...]uint8{0, 9, 18, 39, 54, 67}\n\nfunc (i endState) String() string {\n\tif i < 0 || i >= endState(len(_endState_index)-1) {\n\t\treturn \"endState(\" + strconv.FormatInt(int64(i), 10) + \")\"\n\t}\n\treturn _endState_name[_endState_index[i]:_endState_index[i+1]]\n}\n"
  },
  {
    "path": "chapter/8/rollout/lb/README.md",
    "content": "## Load Balancer Example\n\nWe have added a simple layer 7 Load Balancer here to use in the rollout example from the book. This code is not covered in that chapter, instead it simply covers that you are interacting with a load balancer to add and remove web servers.\n\nTo make the full example work, we have built a simple load balancer using Go with client libraries and sample applications.\n\nThere is also a CLI application for interacting with the load balancer outside the example application.\n\n### Using the Load Balancer\n\nThe load balancer is setup to run on port 8080. Your OS will need to allow this port and nothing else can be running on it.\n\nTo run the load balancer, in this directory simply run:\n```bash\ngo run lb.go\n```\n\n### Interacting with the CLI\n\nWe included a CLI app, which is not required for the example. It allowed us to do basic testing with the load balancer.\n\nThe CLI app will allow you to:\n- Add a pool, which is based on the URL pattern to match against\n- Remove a pool by pattern\n- Add a backend to a pool\n- Remove a backend from a pool\n- Get a pools health\n\nPattern matching is based on the `http` package pattern matching. See the GoDoc for more information.\n\nAdding a pool with two backends looks like:\n```go\n$ go run cli.go --lb=127.0.0.1:8081 --pattern=/ addPool\n$ go run cli.go --lb=127.0.0.1:8081 --pattern=/ --ip=127.0.0.1 --port=8082 --url_path=/ addBackend\n$ go run cli.go --lb=127.0.0.1:8081 --pattern=/ --ip=127.0.0.1 --port=8083 --url_path=/ addBackend\n```\nThis first contacts our load balancer (127.0.0.1:8081) and adds a pool that matches pattern /.\n\nThen it adds two backends running on local ports 8082 and 8083 with a URL path of /.\n\nNote that the CLI sets up a health check that queries the backend's `/healthz` page looking for `ok` in the body of the response. If it doesn't respond, you can't add that backend.  This is also checked at intervals and it will remove unhealthy nodes until they pass a health check.\n\nThere is an example web server you can run in the `sample/web` directory to provide the load balancer with valid backends. Simply go into that directory and run:\n```bash\n$ go run main.go --port=8082\n```\nThis would run a webserver on port 8082.  You can do this multiple times on different ports. In the example above we ran them on 8082 and 8083.\n\nYou can see the health of your pool with:\n```bash\ngo run cli.go --lb=127.0.0.1:8081 --pattern=/ poolHealth\nPool  Status   \n/     PS_FULL  \nBackend         Status      \n127.0.0.1:8082  BS_HEALTHY  \n127.0.0.1:8083  BS_HEALTHY  \n```\n\n### NOTES\n\n- This is not a production level load balancer. It lacks a lot of bells and whistles, monitoring, metrics and most importantly tests.\n- There is no security on the gRPC service."
  },
  {
    "path": "chapter/8/rollout/lb/buf.work",
    "content": "version: v1\ndirectories:\n  - proto\n"
  },
  {
    "path": "chapter/8/rollout/lb/client/cli/cli.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"fmt\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/8/rollout/lb/client\"\n\n\t\"github.com/fatih/color\"\n\t\"github.com/rodaine/table\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/8/rollout/lb/proto\"\n)\n\nvar (\n\tserver  = flag.String(\"lb\", \"\", \"The load balancer address to connect to, host:port\")\n\tip      = flag.String(\"ip\", \"\", \"An IP setting\")\n\tport    = flag.Int(\"port\", 0, \"A port setting\")\n\turlPath = flag.String(\"url_path\", \"\", \"The url path to use\")\n\tpattern = flag.String(\"pattern\", \"\", \"A pattern setting\")\n)\n\nvar hcs = client.HealthChecks{\n\tInterval: 10 * time.Second,\n\tHealthChecks: []client.HealthCheck{\n\t\tclient.StatusCheck{\n\t\t\tURLPath:       \"/healthz\",\n\t\t\tHealthyValues: []string{\"ok\", \"OK\"},\n\t\t},\n\t},\n}\n\nfunc main() {\n\tflag.Parse()\n\n\tif len(flag.Args()) != 1 {\n\t\tpanic(\"bad args\")\n\t}\n\n\tc, err := client.New(*server)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tswitch flag.Args()[0] {\n\tcase \"addPool\":\n\t\tctx, _ := context.WithTimeout(context.Background(), 30*time.Second)\n\t\tif err := c.AddPool(ctx, *pattern, pb.PoolType_PT_P2C, hcs); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\tcase \"removePool\":\n\t\tctx, _ := context.WithTimeout(context.Background(), 30*time.Second)\n\t\tif err := c.RemovePool(ctx, *pattern); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\tcase \"addBackend\":\n\t\tctx, _ := context.WithTimeout(context.Background(), 30*time.Second)\n\n\t\tb := client.IPBackend{\n\t\t\tIP:      net.ParseIP(*ip),\n\t\t\tPort:    int32(*port),\n\t\t\tURLPath: *urlPath,\n\t\t}\n\t\tif err := c.AddBackend(ctx, *pattern, b); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\tcase \"removeBackend\":\n\t\tctx, _ := context.WithTimeout(context.Background(), 30*time.Second)\n\n\t\tb := client.IPBackend{\n\t\t\tIP:      net.ParseIP(*ip),\n\t\t\tPort:    int32(*port),\n\t\t\tURLPath: *urlPath,\n\t\t}\n\t\tif err := c.AddBackend(ctx, *pattern, b); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\tcase \"poolHealth\":\n\t\tctx, _ := context.WithTimeout(context.Background(), 2*time.Second)\n\t\tph, err := c.PoolHealth(ctx, *pattern, true, true)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\theaderFmt := color.New(color.FgGreen, color.Underline).SprintfFunc()\n\t\tcolumnFmt := color.New(color.FgYellow).SprintfFunc()\n\n\t\ttbl := table.New(\"Pool\", \"Status\")\n\t\ttbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt)\n\t\ttbl.AddRow(*pattern, ph.Status)\n\t\ttbl.Print()\n\n\t\ttbl = table.New(\"Backend\", \"Status\")\n\t\ttbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt)\n\t\tfor _, b := range ph.Backends {\n\t\t\tswitch {\n\t\t\tcase b.Backend.GetIpBackend() != nil:\n\t\t\t\tv := b.Backend.GetIpBackend()\n\t\t\t\ttbl.AddRow(\n\t\t\t\t\tfmt.Sprintf(\"%s:%d%s\", v.Ip, v.Port, v.UrlPath),\n\t\t\t\t\tb.Status.String(),\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\ttbl.Print()\n\tdefault:\n\t\tpanic(\"non-recognized command\")\n\t}\n}\n"
  },
  {
    "path": "chapter/8/rollout/lb/client/client.go",
    "content": "package client\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"time\"\n\n\t\"google.golang.org/grpc\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/8/rollout/lb/proto\"\n)\n\n// HealthChecks are a set of backend health checks that a backend must pass\n// in order to be considered healthy.\ntype HealthChecks struct {\n\t// HealthChecks are the checks to run against the backends.\n\tHealthChecks []HealthCheck\n\t// Interval is the between checks.\n\tInterval time.Duration\n}\n\nfunc (h HealthChecks) toPB() *pb.HealthChecks {\n\thcs := &pb.HealthChecks{\n\t\tIntervalSecs: int32(h.Interval / time.Second),\n\t}\n\tfor _, hc := range h.HealthChecks {\n\t\tp := hc.toPB()\n\t\thcs.HealthChecks = append(hcs.HealthChecks, p)\n\t}\n\treturn hcs\n}\n\n// HealthCheck defines a health check that must pass for a backend in a Pool\n// to be considered healthy.\ntype HealthCheck interface {\n\ttoPB() *pb.HealthCheck\n\tisHealthCheck()\n}\n\n// StatusCheck implements HealthCheck to check a node at \"URLPath\" for\n// any string in \"HealthyValues\". If found, the node is said to be healthy.\ntype StatusCheck struct {\n\t// URLPath is the path to the health status page, like \"/health\".\n\tURLPath string\n\t// HealthValues are values that the URLPath can return and the service\n\t// is considered healthy.\n\tHealthyValues []string\n}\n\nfunc (s StatusCheck) toPB() *pb.HealthCheck {\n\treturn &pb.HealthCheck{\n\t\tHealthCheck: &pb.HealthCheck_StatusCheck{\n\t\t\tStatusCheck: &pb.StatusCheck{\n\t\t\t\tUrlPath:       s.URLPath,\n\t\t\t\tHealthyValues: s.HealthyValues,\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (s StatusCheck) isHealthCheck() {}\n\n// Client is a client to the Quote of the day server.\ntype Client struct {\n\tclient pb.LoadBalancerClient\n\tconn   *grpc.ClientConn\n}\n\n// New is the constructor for Client. addr is the server's [host]:[port].\nfunc New(addr string) (*Client, error) {\n\tconn, err := grpc.Dial(addr, grpc.WithInsecure())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Client{\n\t\tclient: pb.NewLoadBalancerClient(conn),\n\t\tconn:   conn,\n\t}, nil\n}\n\n// AddPool adds a pool that serves \"pattern\" using a PoolType that controls how\n// the pool load balances traffic and a HealthCheck to determine if a node is healthy.\nfunc (c *Client) AddPool(ctx context.Context, pattern string, pt pb.PoolType, hcs HealthChecks) error {\n\tif _, ok := ctx.Deadline(); !ok {\n\t\tvar cancel context.CancelFunc\n\t\tctx, cancel = context.WithTimeout(ctx, 30*time.Second)\n\t\tdefer cancel()\n\t}\n\n\t_, err := c.client.AddPool(\n\t\tctx,\n\t\t&pb.AddPoolReq{\n\t\t\tPattern:      pattern,\n\t\t\tPoolType:     pt,\n\t\t\tHealthChecks: hcs.toPB(),\n\t\t},\n\t)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// RemovePool removes the pool that is serving \"pattern\".\nfunc (c *Client) RemovePool(ctx context.Context, pattern string) error {\n\tif _, ok := ctx.Deadline(); !ok {\n\t\tvar cancel context.CancelFunc\n\t\tctx, cancel = context.WithTimeout(ctx, 30*time.Second)\n\t\tdefer cancel()\n\t}\n\n\t_, err := c.client.RemovePool(ctx, &pb.RemovePoolReq{Pattern: pattern})\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// Backend represents a load balancer backend.\ntype Backend interface {\n\tisBackend()\n}\n\n// IPBackend implements Backend.\ntype IPBackend struct {\n\tIP      net.IP\n\tPort    int32\n\tURLPath string\n}\n\nfunc (i IPBackend) isBackend() {}\n\n// AddBackend adds backend \"b\" from the pool serving \"pattern\".\nfunc (c *Client) AddBackend(ctx context.Context, pattern string, b Backend) error {\n\tswitch v := b.(type) {\n\tcase IPBackend:\n\t\treturn c.addIPBackend(ctx, pattern, v)\n\t}\n\treturn fmt.Errorf(\"Backend is not a recognized type(%T)\", b)\n}\n\nfunc (c *Client) addIPBackend(ctx context.Context, pattern string, b IPBackend) error {\n\t_, err := c.client.AddBackend(\n\t\tctx,\n\t\t&pb.AddBackendReq{\n\t\t\tPattern: pattern,\n\t\t\tBackend: &pb.Backend{\n\t\t\t\tBackend: &pb.Backend_IpBackend{\n\t\t\t\t\tIpBackend: &pb.IPBackend{\n\t\t\t\t\t\tIp:      b.IP.String(),\n\t\t\t\t\t\tPort:    b.Port,\n\t\t\t\t\t\tUrlPath: b.URLPath,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// RemoveBackend removes backend \"b\" from the pool serving \"pattern\".\nfunc (c *Client) RemoveBackend(ctx context.Context, pattern string, b Backend) error {\n\tswitch v := b.(type) {\n\tcase IPBackend:\n\t\treturn c.removeIPBackend(ctx, pattern, v)\n\t}\n\treturn fmt.Errorf(\"Backend is not a recognized type(%T)\", b)\n}\n\nfunc (c *Client) removeIPBackend(ctx context.Context, pattern string, b IPBackend) error {\n\t_, err := c.client.RemoveBackend(\n\t\tctx,\n\t\t&pb.RemoveBackendReq{\n\t\t\tPattern: pattern,\n\t\t\tBackend: &pb.Backend{\n\t\t\t\tBackend: &pb.Backend_IpBackend{\n\t\t\t\t\tIpBackend: &pb.IPBackend{\n\t\t\t\t\t\tIp:      b.IP.String(),\n\t\t\t\t\t\tPort:    b.Port,\n\t\t\t\t\t\tUrlPath: b.URLPath,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// PoolHealth queries the server for the health of the pool that serves \"pattern\".\n// healthy and sick determine what node information is included.\nfunc (c *Client) PoolHealth(ctx context.Context, pattern string, healthy, sick bool) (*pb.PoolHealth, error) {\n\tresp, err := c.client.PoolHealth(\n\t\tctx,\n\t\t&pb.PoolHealthReq{\n\t\t\tPattern: pattern,\n\t\t\tHealthy: healthy,\n\t\t\tSick:    sick,\n\t\t},\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn resp.Health, nil\n}\n"
  },
  {
    "path": "chapter/8/rollout/lb/lb.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/8/rollout/lb/server/grpc\"\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/8/rollout/lb/server/http\"\n)\n\nfunc main() {\n\tln, err := net.Listen(\"tcp\", \":8080\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tlb, err := http.New()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tlog.Println(\"load balancer started(8080)...\")\n\tgo func() {\n\t\tif err := lb.Serve(ln); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}()\n\n\tserv, err := grpc.New(\":8081\", lb)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tlog.Println(\"grpc server started(8081)...\")\n\tif err := serv.Start(); err != nil {\n\t\tpanic(err)\n\t}\n}\n"
  },
  {
    "path": "chapter/8/rollout/lb/proto/buf.gen.yaml",
    "content": "version: v1\nplugins:\n  - name: go\n    out: ./\n    opt:\n      - paths=source_relative\n  - name: go-grpc\n    out: ./\n    opt:\n      - paths=source_relative\n"
  },
  {
    "path": "chapter/8/rollout/lb/proto/buf.yaml",
    "content": "version: v1\nlint:\n  use:\n    - DEFAULT\nbreaking:\n  use:\n    - FILE\n"
  },
  {
    "path": "chapter/8/rollout/lb/proto/lb.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.25.0-devel\n// \tprotoc        v3.18.0\n// source: lb.proto\n\npackage lb\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\n// PoolType indicates what type of pool to use. This influences\n// how the pool distributes its workload.\ntype PoolType int32\n\nconst (\n\t// This indicates an error by the user.\n\tPoolType_PT_UNKNOWN PoolType = 0\n\t// The power of 2 choices selection pool.\n\tPoolType_PT_P2C PoolType = 1\n)\n\n// Enum value maps for PoolType.\nvar (\n\tPoolType_name = map[int32]string{\n\t\t0: \"PT_UNKNOWN\",\n\t\t1: \"PT_P2C\",\n\t}\n\tPoolType_value = map[string]int32{\n\t\t\"PT_UNKNOWN\": 0,\n\t\t\"PT_P2C\":     1,\n\t}\n)\n\nfunc (x PoolType) Enum() *PoolType {\n\tp := new(PoolType)\n\t*p = x\n\treturn p\n}\n\nfunc (x PoolType) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (PoolType) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_lb_proto_enumTypes[0].Descriptor()\n}\n\nfunc (PoolType) Type() protoreflect.EnumType {\n\treturn &file_lb_proto_enumTypes[0]\n}\n\nfunc (x PoolType) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use PoolType.Descriptor instead.\nfunc (PoolType) EnumDescriptor() ([]byte, []int) {\n\treturn file_lb_proto_rawDescGZIP(), []int{0}\n}\n\ntype PoolStatus int32\n\nconst (\n\t// This indicates an error by the developers.\n\tPoolStatus_PS_UNKNOWN PoolStatus = 0\n\t// The pool has all its configured backends working.\n\tPoolStatus_PS_FULL PoolStatus = 1\n\t// The pool has no configured backends.\n\tPoolStatus_PS_EMPTY PoolStatus = 2\n\t// The pool has one or more configured backends not working.\n\tPoolStatus_PS_DEGRADED PoolStatus = 3\n)\n\n// Enum value maps for PoolStatus.\nvar (\n\tPoolStatus_name = map[int32]string{\n\t\t0: \"PS_UNKNOWN\",\n\t\t1: \"PS_FULL\",\n\t\t2: \"PS_EMPTY\",\n\t\t3: \"PS_DEGRADED\",\n\t}\n\tPoolStatus_value = map[string]int32{\n\t\t\"PS_UNKNOWN\":  0,\n\t\t\"PS_FULL\":     1,\n\t\t\"PS_EMPTY\":    2,\n\t\t\"PS_DEGRADED\": 3,\n\t}\n)\n\nfunc (x PoolStatus) Enum() *PoolStatus {\n\tp := new(PoolStatus)\n\t*p = x\n\treturn p\n}\n\nfunc (x PoolStatus) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (PoolStatus) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_lb_proto_enumTypes[1].Descriptor()\n}\n\nfunc (PoolStatus) Type() protoreflect.EnumType {\n\treturn &file_lb_proto_enumTypes[1]\n}\n\nfunc (x PoolStatus) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use PoolStatus.Descriptor instead.\nfunc (PoolStatus) EnumDescriptor() ([]byte, []int) {\n\treturn file_lb_proto_rawDescGZIP(), []int{1}\n}\n\n// BackendStatus details the status of a backend.\ntype BackendStatus int32\n\nconst (\n\t// This indicates an error by the developers.\n\tBackendStatus_BS_UNKNOWN BackendStatus = 0\n\t// The node is healthy according to its health checks.\n\tBackendStatus_BS_HEALTHY BackendStatus = 1\n\t// The node is sick according to its health checks.\n\tBackendStatus_BS_SICK BackendStatus = 2\n)\n\n// Enum value maps for BackendStatus.\nvar (\n\tBackendStatus_name = map[int32]string{\n\t\t0: \"BS_UNKNOWN\",\n\t\t1: \"BS_HEALTHY\",\n\t\t2: \"BS_SICK\",\n\t}\n\tBackendStatus_value = map[string]int32{\n\t\t\"BS_UNKNOWN\": 0,\n\t\t\"BS_HEALTHY\": 1,\n\t\t\"BS_SICK\":    2,\n\t}\n)\n\nfunc (x BackendStatus) Enum() *BackendStatus {\n\tp := new(BackendStatus)\n\t*p = x\n\treturn p\n}\n\nfunc (x BackendStatus) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (BackendStatus) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_lb_proto_enumTypes[2].Descriptor()\n}\n\nfunc (BackendStatus) Type() protoreflect.EnumType {\n\treturn &file_lb_proto_enumTypes[2]\n}\n\nfunc (x BackendStatus) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use BackendStatus.Descriptor instead.\nfunc (BackendStatus) EnumDescriptor() ([]byte, []int) {\n\treturn file_lb_proto_rawDescGZIP(), []int{2}\n}\n\ntype HealthChecks struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tHealthChecks []*HealthCheck `protobuf:\"bytes,1,rep,name=health_checks,json=healthChecks,proto3\" json:\"health_checks,omitempty\"`\n\tIntervalSecs int32          `protobuf:\"varint,2,opt,name=interval_secs,json=intervalSecs,proto3\" json:\"interval_secs,omitempty\"`\n}\n\nfunc (x *HealthChecks) Reset() {\n\t*x = HealthChecks{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_lb_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *HealthChecks) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*HealthChecks) ProtoMessage() {}\n\nfunc (x *HealthChecks) ProtoReflect() protoreflect.Message {\n\tmi := &file_lb_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 HealthChecks.ProtoReflect.Descriptor instead.\nfunc (*HealthChecks) Descriptor() ([]byte, []int) {\n\treturn file_lb_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *HealthChecks) GetHealthChecks() []*HealthCheck {\n\tif x != nil {\n\t\treturn x.HealthChecks\n\t}\n\treturn nil\n}\n\nfunc (x *HealthChecks) GetIntervalSecs() int32 {\n\tif x != nil {\n\t\treturn x.IntervalSecs\n\t}\n\treturn 0\n}\n\ntype HealthCheck struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Types that are assignable to HealthCheck:\n\t//\t*HealthCheck_StatusCheck\n\tHealthCheck isHealthCheck_HealthCheck `protobuf_oneof:\"health_check\"`\n}\n\nfunc (x *HealthCheck) Reset() {\n\t*x = HealthCheck{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_lb_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *HealthCheck) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*HealthCheck) ProtoMessage() {}\n\nfunc (x *HealthCheck) ProtoReflect() protoreflect.Message {\n\tmi := &file_lb_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 HealthCheck.ProtoReflect.Descriptor instead.\nfunc (*HealthCheck) Descriptor() ([]byte, []int) {\n\treturn file_lb_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (m *HealthCheck) GetHealthCheck() isHealthCheck_HealthCheck {\n\tif m != nil {\n\t\treturn m.HealthCheck\n\t}\n\treturn nil\n}\n\nfunc (x *HealthCheck) GetStatusCheck() *StatusCheck {\n\tif x, ok := x.GetHealthCheck().(*HealthCheck_StatusCheck); ok {\n\t\treturn x.StatusCheck\n\t}\n\treturn nil\n}\n\ntype isHealthCheck_HealthCheck interface {\n\tisHealthCheck_HealthCheck()\n}\n\ntype HealthCheck_StatusCheck struct {\n\tStatusCheck *StatusCheck `protobuf:\"bytes,1,opt,name=status_check,json=statusCheck,proto3,oneof\"`\n}\n\nfunc (*HealthCheck_StatusCheck) isHealthCheck_HealthCheck() {}\n\n// StatusCheck is a check against a URL path. That path must\n// emit in its body one of the healthy_values or it fails.\ntype StatusCheck struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tUrlPath       string   `protobuf:\"bytes,1,opt,name=url_path,json=urlPath,proto3\" json:\"url_path,omitempty\"`\n\tHealthyValues []string `protobuf:\"bytes,2,rep,name=healthy_values,json=healthyValues,proto3\" json:\"healthy_values,omitempty\"`\n}\n\nfunc (x *StatusCheck) Reset() {\n\t*x = StatusCheck{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_lb_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *StatusCheck) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*StatusCheck) ProtoMessage() {}\n\nfunc (x *StatusCheck) ProtoReflect() protoreflect.Message {\n\tmi := &file_lb_proto_msgTypes[2]\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 StatusCheck.ProtoReflect.Descriptor instead.\nfunc (*StatusCheck) Descriptor() ([]byte, []int) {\n\treturn file_lb_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *StatusCheck) GetUrlPath() string {\n\tif x != nil {\n\t\treturn x.UrlPath\n\t}\n\treturn \"\"\n}\n\nfunc (x *StatusCheck) GetHealthyValues() []string {\n\tif x != nil {\n\t\treturn x.HealthyValues\n\t}\n\treturn nil\n}\n\ntype Backend struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Types that are assignable to Backend:\n\t//\t*Backend_IpBackend\n\tBackend isBackend_Backend `protobuf_oneof:\"backend\"`\n}\n\nfunc (x *Backend) Reset() {\n\t*x = Backend{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_lb_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Backend) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Backend) ProtoMessage() {}\n\nfunc (x *Backend) ProtoReflect() protoreflect.Message {\n\tmi := &file_lb_proto_msgTypes[3]\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 Backend.ProtoReflect.Descriptor instead.\nfunc (*Backend) Descriptor() ([]byte, []int) {\n\treturn file_lb_proto_rawDescGZIP(), []int{3}\n}\n\nfunc (m *Backend) GetBackend() isBackend_Backend {\n\tif m != nil {\n\t\treturn m.Backend\n\t}\n\treturn nil\n}\n\nfunc (x *Backend) GetIpBackend() *IPBackend {\n\tif x, ok := x.GetBackend().(*Backend_IpBackend); ok {\n\t\treturn x.IpBackend\n\t}\n\treturn nil\n}\n\ntype isBackend_Backend interface {\n\tisBackend_Backend()\n}\n\ntype Backend_IpBackend struct {\n\tIpBackend *IPBackend `protobuf:\"bytes,1,opt,name=ip_backend,json=ipBackend,proto3,oneof\"`\n}\n\nfunc (*Backend_IpBackend) isBackend_Backend() {}\n\n// IPBackend provides a Backend that has its endpoint as an ip:port.\ntype IPBackend struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The IP(v4 or v6) to use as the host.\n\tIp string `protobuf:\"bytes,1,opt,name=ip,proto3\" json:\"ip,omitempty\"`\n\t// The port number to connect on.\n\tPort int32 `protobuf:\"varint,2,opt,name=port,proto3\" json:\"port,omitempty\"`\n\t// The url_path to forward to. Generally this is empty.\n\tUrlPath string `protobuf:\"bytes,3,opt,name=url_path,json=urlPath,proto3\" json:\"url_path,omitempty\"`\n}\n\nfunc (x *IPBackend) Reset() {\n\t*x = IPBackend{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_lb_proto_msgTypes[4]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *IPBackend) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*IPBackend) ProtoMessage() {}\n\nfunc (x *IPBackend) ProtoReflect() protoreflect.Message {\n\tmi := &file_lb_proto_msgTypes[4]\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 IPBackend.ProtoReflect.Descriptor instead.\nfunc (*IPBackend) Descriptor() ([]byte, []int) {\n\treturn file_lb_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (x *IPBackend) GetIp() string {\n\tif x != nil {\n\t\treturn x.Ip\n\t}\n\treturn \"\"\n}\n\nfunc (x *IPBackend) GetPort() int32 {\n\tif x != nil {\n\t\treturn x.Port\n\t}\n\treturn 0\n}\n\nfunc (x *IPBackend) GetUrlPath() string {\n\tif x != nil {\n\t\treturn x.UrlPath\n\t}\n\treturn \"\"\n}\n\n// PoolHealth details the health status of a pool.\ntype PoolHealth struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The status of the pool.\n\tStatus PoolStatus `protobuf:\"varint,1,opt,name=status,proto3,enum=rollout.lb.PoolStatus\" json:\"status,omitempty\"`\n\t// The pool's backend health status. This can contain all backends\n\t// or only sick/healthy backends, depending on the request.\n\tBackends []*BackendHealth `protobuf:\"bytes,2,rep,name=backends,proto3\" json:\"backends,omitempty\"`\n}\n\nfunc (x *PoolHealth) Reset() {\n\t*x = PoolHealth{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_lb_proto_msgTypes[5]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *PoolHealth) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*PoolHealth) ProtoMessage() {}\n\nfunc (x *PoolHealth) ProtoReflect() protoreflect.Message {\n\tmi := &file_lb_proto_msgTypes[5]\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 PoolHealth.ProtoReflect.Descriptor instead.\nfunc (*PoolHealth) Descriptor() ([]byte, []int) {\n\treturn file_lb_proto_rawDescGZIP(), []int{5}\n}\n\nfunc (x *PoolHealth) GetStatus() PoolStatus {\n\tif x != nil {\n\t\treturn x.Status\n\t}\n\treturn PoolStatus_PS_UNKNOWN\n}\n\nfunc (x *PoolHealth) GetBackends() []*BackendHealth {\n\tif x != nil {\n\t\treturn x.Backends\n\t}\n\treturn nil\n}\n\n// BackendHealth details the health status of a backend.\ntype BackendHealth struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tBackend *Backend      `protobuf:\"bytes,1,opt,name=backend,proto3\" json:\"backend,omitempty\"`\n\tStatus  BackendStatus `protobuf:\"varint,2,opt,name=status,proto3,enum=rollout.lb.BackendStatus\" json:\"status,omitempty\"`\n}\n\nfunc (x *BackendHealth) Reset() {\n\t*x = BackendHealth{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_lb_proto_msgTypes[6]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *BackendHealth) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*BackendHealth) ProtoMessage() {}\n\nfunc (x *BackendHealth) ProtoReflect() protoreflect.Message {\n\tmi := &file_lb_proto_msgTypes[6]\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 BackendHealth.ProtoReflect.Descriptor instead.\nfunc (*BackendHealth) Descriptor() ([]byte, []int) {\n\treturn file_lb_proto_rawDescGZIP(), []int{6}\n}\n\nfunc (x *BackendHealth) GetBackend() *Backend {\n\tif x != nil {\n\t\treturn x.Backend\n\t}\n\treturn nil\n}\n\nfunc (x *BackendHealth) GetStatus() BackendStatus {\n\tif x != nil {\n\t\treturn x.Status\n\t}\n\treturn BackendStatus_BS_UNKNOWN\n}\n\n// AddPoolReq requests to create a pool for handling requests.\ntype AddPoolReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The URL pattern to direct traffic too.\n\tPattern string `protobuf:\"bytes,1,opt,name=pattern,proto3\" json:\"pattern,omitempty\"`\n\t// The type of traffic distribution pool to use.\n\tPoolType PoolType `protobuf:\"varint,2,opt,name=pool_type,json=poolType,proto3,enum=rollout.lb.PoolType\" json:\"pool_type,omitempty\"`\n\t// Health checks to against backends.\n\tHealthChecks *HealthChecks `protobuf:\"bytes,4,opt,name=health_checks,json=healthChecks,proto3\" json:\"health_checks,omitempty\"`\n}\n\nfunc (x *AddPoolReq) Reset() {\n\t*x = AddPoolReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_lb_proto_msgTypes[7]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AddPoolReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AddPoolReq) ProtoMessage() {}\n\nfunc (x *AddPoolReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_lb_proto_msgTypes[7]\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 AddPoolReq.ProtoReflect.Descriptor instead.\nfunc (*AddPoolReq) Descriptor() ([]byte, []int) {\n\treturn file_lb_proto_rawDescGZIP(), []int{7}\n}\n\nfunc (x *AddPoolReq) GetPattern() string {\n\tif x != nil {\n\t\treturn x.Pattern\n\t}\n\treturn \"\"\n}\n\nfunc (x *AddPoolReq) GetPoolType() PoolType {\n\tif x != nil {\n\t\treturn x.PoolType\n\t}\n\treturn PoolType_PT_UNKNOWN\n}\n\nfunc (x *AddPoolReq) GetHealthChecks() *HealthChecks {\n\tif x != nil {\n\t\treturn x.HealthChecks\n\t}\n\treturn nil\n}\n\n// AddPoolResp is the response to adding a pool.\ntype AddPoolResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *AddPoolResp) Reset() {\n\t*x = AddPoolResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_lb_proto_msgTypes[8]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AddPoolResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AddPoolResp) ProtoMessage() {}\n\nfunc (x *AddPoolResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_lb_proto_msgTypes[8]\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 AddPoolResp.ProtoReflect.Descriptor instead.\nfunc (*AddPoolResp) Descriptor() ([]byte, []int) {\n\treturn file_lb_proto_rawDescGZIP(), []int{8}\n}\n\n// RemovePoolReq is used to remove a pool by its pattern.\ntype RemovePoolReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Pattern is the pattern to remove.\n\tPattern string `protobuf:\"bytes,1,opt,name=pattern,proto3\" json:\"pattern,omitempty\"`\n}\n\nfunc (x *RemovePoolReq) Reset() {\n\t*x = RemovePoolReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_lb_proto_msgTypes[9]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *RemovePoolReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*RemovePoolReq) ProtoMessage() {}\n\nfunc (x *RemovePoolReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_lb_proto_msgTypes[9]\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 RemovePoolReq.ProtoReflect.Descriptor instead.\nfunc (*RemovePoolReq) Descriptor() ([]byte, []int) {\n\treturn file_lb_proto_rawDescGZIP(), []int{9}\n}\n\nfunc (x *RemovePoolReq) GetPattern() string {\n\tif x != nil {\n\t\treturn x.Pattern\n\t}\n\treturn \"\"\n}\n\n// RemovePoolResp is the response to removing a pool.\ntype RemovePoolResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *RemovePoolResp) Reset() {\n\t*x = RemovePoolResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_lb_proto_msgTypes[10]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *RemovePoolResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*RemovePoolResp) ProtoMessage() {}\n\nfunc (x *RemovePoolResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_lb_proto_msgTypes[10]\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 RemovePoolResp.ProtoReflect.Descriptor instead.\nfunc (*RemovePoolResp) Descriptor() ([]byte, []int) {\n\treturn file_lb_proto_rawDescGZIP(), []int{10}\n}\n\n// AddBackendReq adds a backend to a pool.\ntype AddBackendReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The pattern to add a backend to.\n\tPattern string `protobuf:\"bytes,1,opt,name=pattern,proto3\" json:\"pattern,omitempty\"`\n\t// The backend to add to the pool.\n\tBackend *Backend `protobuf:\"bytes,2,opt,name=backend,proto3\" json:\"backend,omitempty\"`\n}\n\nfunc (x *AddBackendReq) Reset() {\n\t*x = AddBackendReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_lb_proto_msgTypes[11]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AddBackendReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AddBackendReq) ProtoMessage() {}\n\nfunc (x *AddBackendReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_lb_proto_msgTypes[11]\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 AddBackendReq.ProtoReflect.Descriptor instead.\nfunc (*AddBackendReq) Descriptor() ([]byte, []int) {\n\treturn file_lb_proto_rawDescGZIP(), []int{11}\n}\n\nfunc (x *AddBackendReq) GetPattern() string {\n\tif x != nil {\n\t\treturn x.Pattern\n\t}\n\treturn \"\"\n}\n\nfunc (x *AddBackendReq) GetBackend() *Backend {\n\tif x != nil {\n\t\treturn x.Backend\n\t}\n\treturn nil\n}\n\ntype AddBackendResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *AddBackendResp) Reset() {\n\t*x = AddBackendResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_lb_proto_msgTypes[12]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *AddBackendResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*AddBackendResp) ProtoMessage() {}\n\nfunc (x *AddBackendResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_lb_proto_msgTypes[12]\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 AddBackendResp.ProtoReflect.Descriptor instead.\nfunc (*AddBackendResp) Descriptor() ([]byte, []int) {\n\treturn file_lb_proto_rawDescGZIP(), []int{12}\n}\n\n// RemoveBackendReq is used to remove a Backend from a Pool.\ntype RemoveBackendReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// The pool pattern to remove the backend from.\n\tPattern string `protobuf:\"bytes,1,opt,name=pattern,proto3\" json:\"pattern,omitempty\"`\n\t// The backend to remove from the pool.\n\tBackend *Backend `protobuf:\"bytes,2,opt,name=backend,proto3\" json:\"backend,omitempty\"`\n}\n\nfunc (x *RemoveBackendReq) Reset() {\n\t*x = RemoveBackendReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_lb_proto_msgTypes[13]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *RemoveBackendReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*RemoveBackendReq) ProtoMessage() {}\n\nfunc (x *RemoveBackendReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_lb_proto_msgTypes[13]\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 RemoveBackendReq.ProtoReflect.Descriptor instead.\nfunc (*RemoveBackendReq) Descriptor() ([]byte, []int) {\n\treturn file_lb_proto_rawDescGZIP(), []int{13}\n}\n\nfunc (x *RemoveBackendReq) GetPattern() string {\n\tif x != nil {\n\t\treturn x.Pattern\n\t}\n\treturn \"\"\n}\n\nfunc (x *RemoveBackendReq) GetBackend() *Backend {\n\tif x != nil {\n\t\treturn x.Backend\n\t}\n\treturn nil\n}\n\n// RemoveBackendResp is the response to removing a Backend.\ntype RemoveBackendResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *RemoveBackendResp) Reset() {\n\t*x = RemoveBackendResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_lb_proto_msgTypes[14]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *RemoveBackendResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*RemoveBackendResp) ProtoMessage() {}\n\nfunc (x *RemoveBackendResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_lb_proto_msgTypes[14]\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 RemoveBackendResp.ProtoReflect.Descriptor instead.\nfunc (*RemoveBackendResp) Descriptor() ([]byte, []int) {\n\treturn file_lb_proto_rawDescGZIP(), []int{14}\n}\n\n// PoolHealthReq is a request to get the health of a pool.\ntype PoolHealthReq struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Pattern is the pool pattern you are getting health for.\n\tPattern string `protobuf:\"bytes,1,opt,name=pattern,proto3\" json:\"pattern,omitempty\"`\n\t// If set to true, will return the backends that are healthy.\n\tHealthy bool `protobuf:\"varint,3,opt,name=healthy,proto3\" json:\"healthy,omitempty\"`\n\t// If set to true, will return backends that are sick.\n\tSick bool `protobuf:\"varint,4,opt,name=sick,proto3\" json:\"sick,omitempty\"`\n}\n\nfunc (x *PoolHealthReq) Reset() {\n\t*x = PoolHealthReq{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_lb_proto_msgTypes[15]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *PoolHealthReq) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*PoolHealthReq) ProtoMessage() {}\n\nfunc (x *PoolHealthReq) ProtoReflect() protoreflect.Message {\n\tmi := &file_lb_proto_msgTypes[15]\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 PoolHealthReq.ProtoReflect.Descriptor instead.\nfunc (*PoolHealthReq) Descriptor() ([]byte, []int) {\n\treturn file_lb_proto_rawDescGZIP(), []int{15}\n}\n\nfunc (x *PoolHealthReq) GetPattern() string {\n\tif x != nil {\n\t\treturn x.Pattern\n\t}\n\treturn \"\"\n}\n\nfunc (x *PoolHealthReq) GetHealthy() bool {\n\tif x != nil {\n\t\treturn x.Healthy\n\t}\n\treturn false\n}\n\nfunc (x *PoolHealthReq) GetSick() bool {\n\tif x != nil {\n\t\treturn x.Sick\n\t}\n\treturn false\n}\n\ntype PoolHealthResp struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tHealth *PoolHealth `protobuf:\"bytes,1,opt,name=health,proto3\" json:\"health,omitempty\"`\n}\n\nfunc (x *PoolHealthResp) Reset() {\n\t*x = PoolHealthResp{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_lb_proto_msgTypes[16]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *PoolHealthResp) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*PoolHealthResp) ProtoMessage() {}\n\nfunc (x *PoolHealthResp) ProtoReflect() protoreflect.Message {\n\tmi := &file_lb_proto_msgTypes[16]\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 PoolHealthResp.ProtoReflect.Descriptor instead.\nfunc (*PoolHealthResp) Descriptor() ([]byte, []int) {\n\treturn file_lb_proto_rawDescGZIP(), []int{16}\n}\n\nfunc (x *PoolHealthResp) GetHealth() *PoolHealth {\n\tif x != nil {\n\t\treturn x.Health\n\t}\n\treturn nil\n}\n\nvar File_lb_proto protoreflect.FileDescriptor\n\nvar file_lb_proto_rawDesc = []byte{\n\t0x0a, 0x08, 0x6c, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x72, 0x6f, 0x6c, 0x6c,\n\t0x6f, 0x75, 0x74, 0x2e, 0x6c, 0x62, 0x22, 0x71, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68,\n\t0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x12, 0x3c, 0x0a, 0x0d, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68,\n\t0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e,\n\t0x72, 0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x2e, 0x6c, 0x62, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74,\n\t0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x0c, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68,\n\t0x65, 0x63, 0x6b, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c,\n\t0x5f, 0x73, 0x65, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x69, 0x6e, 0x74,\n\t0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x73, 0x22, 0x5b, 0x0a, 0x0b, 0x48, 0x65, 0x61,\n\t0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x3c, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74,\n\t0x75, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17,\n\t0x2e, 0x72, 0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x2e, 0x6c, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74,\n\t0x75, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75,\n\t0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x0e, 0x0a, 0x0c, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68,\n\t0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x22, 0x4f, 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,\n\t0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x72, 0x6c, 0x5f, 0x70, 0x61, 0x74,\n\t0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x75, 0x72, 0x6c, 0x50, 0x61, 0x74, 0x68,\n\t0x12, 0x25, 0x0a, 0x0e, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x5f, 0x76, 0x61, 0x6c, 0x75,\n\t0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68,\n\t0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x4c, 0x0a, 0x07, 0x42, 0x61, 0x63, 0x6b, 0x65,\n\t0x6e, 0x64, 0x12, 0x36, 0x0a, 0x0a, 0x69, 0x70, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74,\n\t0x2e, 0x6c, 0x62, 0x2e, 0x49, 0x50, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x48, 0x00, 0x52,\n\t0x09, 0x69, 0x70, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x42, 0x09, 0x0a, 0x07, 0x62, 0x61,\n\t0x63, 0x6b, 0x65, 0x6e, 0x64, 0x22, 0x4a, 0x0a, 0x09, 0x49, 0x50, 0x42, 0x61, 0x63, 0x6b, 0x65,\n\t0x6e, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02,\n\t0x69, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05,\n\t0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x72, 0x6c, 0x5f, 0x70, 0x61,\n\t0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x75, 0x72, 0x6c, 0x50, 0x61, 0x74,\n\t0x68, 0x22, 0x73, 0x0a, 0x0a, 0x50, 0x6f, 0x6f, 0x6c, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12,\n\t0x2e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,\n\t0x16, 0x2e, 0x72, 0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x2e, 0x6c, 0x62, 0x2e, 0x50, 0x6f, 0x6f,\n\t0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12,\n\t0x35, 0x0a, 0x08, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,\n\t0x0b, 0x32, 0x19, 0x2e, 0x72, 0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x2e, 0x6c, 0x62, 0x2e, 0x42,\n\t0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x08, 0x62, 0x61,\n\t0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x22, 0x71, 0x0a, 0x0d, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e,\n\t0x64, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x2d, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65,\n\t0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x72, 0x6f, 0x6c, 0x6c, 0x6f,\n\t0x75, 0x74, 0x2e, 0x6c, 0x62, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x07, 0x62,\n\t0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x31, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,\n\t0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x72, 0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74,\n\t0x2e, 0x6c, 0x62, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75,\n\t0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x98, 0x01, 0x0a, 0x0a, 0x41, 0x64,\n\t0x64, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74,\n\t0x65, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65,\n\t0x72, 0x6e, 0x12, 0x31, 0x0a, 0x09, 0x70, 0x6f, 0x6f, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x72, 0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x2e,\n\t0x6c, 0x62, 0x2e, 0x50, 0x6f, 0x6f, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x70, 0x6f, 0x6f,\n\t0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x3d, 0x0a, 0x0d, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x5f,\n\t0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x72,\n\t0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x2e, 0x6c, 0x62, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68,\n\t0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x52, 0x0c, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68,\n\t0x65, 0x63, 0x6b, 0x73, 0x22, 0x0d, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x6f, 0x6c, 0x52,\n\t0x65, 0x73, 0x70, 0x22, 0x29, 0x0a, 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x6f,\n\t0x6c, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18,\n\t0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x22, 0x10,\n\t0x0a, 0x0e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70,\n\t0x22, 0x58, 0x0a, 0x0d, 0x41, 0x64, 0x64, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65,\n\t0x71, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12, 0x2d, 0x0a, 0x07, 0x62,\n\t0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x72,\n\t0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x2e, 0x6c, 0x62, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e,\n\t0x64, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x22, 0x10, 0x0a, 0x0e, 0x41, 0x64,\n\t0x64, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x22, 0x5b, 0x0a, 0x10,\n\t0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71,\n\t0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12, 0x2d, 0x0a, 0x07, 0x62, 0x61,\n\t0x63, 0x6b, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x72, 0x6f,\n\t0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x2e, 0x6c, 0x62, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64,\n\t0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x22, 0x13, 0x0a, 0x11, 0x52, 0x65, 0x6d,\n\t0x6f, 0x76, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x22, 0x57,\n\t0x0a, 0x0d, 0x50, 0x6f, 0x6f, 0x6c, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x12,\n\t0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,\n\t0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x65, 0x61,\n\t0x6c, 0x74, 0x68, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x68, 0x65, 0x61, 0x6c,\n\t0x74, 0x68, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28,\n\t0x08, 0x52, 0x04, 0x73, 0x69, 0x63, 0x6b, 0x22, 0x40, 0x0a, 0x0e, 0x50, 0x6f, 0x6f, 0x6c, 0x48,\n\t0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x12, 0x2e, 0x0a, 0x06, 0x68, 0x65, 0x61,\n\t0x6c, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x6f, 0x6c, 0x6c,\n\t0x6f, 0x75, 0x74, 0x2e, 0x6c, 0x62, 0x2e, 0x50, 0x6f, 0x6f, 0x6c, 0x48, 0x65, 0x61, 0x6c, 0x74,\n\t0x68, 0x52, 0x06, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x2a, 0x26, 0x0a, 0x08, 0x50, 0x6f, 0x6f,\n\t0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x54, 0x5f, 0x55, 0x4e, 0x4b, 0x4e,\n\t0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x54, 0x5f, 0x50, 0x32, 0x43, 0x10,\n\t0x01, 0x2a, 0x48, 0x0a, 0x0a, 0x50, 0x6f, 0x6f, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12,\n\t0x0e, 0x0a, 0x0a, 0x50, 0x53, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12,\n\t0x0b, 0x0a, 0x07, 0x50, 0x53, 0x5f, 0x46, 0x55, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08,\n\t0x50, 0x53, 0x5f, 0x45, 0x4d, 0x50, 0x54, 0x59, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x53,\n\t0x5f, 0x44, 0x45, 0x47, 0x52, 0x41, 0x44, 0x45, 0x44, 0x10, 0x03, 0x2a, 0x3c, 0x0a, 0x0d, 0x42,\n\t0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0e, 0x0a, 0x0a,\n\t0x42, 0x53, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a,\n\t0x42, 0x53, 0x5f, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x59, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07,\n\t0x42, 0x53, 0x5f, 0x53, 0x49, 0x43, 0x4b, 0x10, 0x02, 0x32, 0xf1, 0x02, 0x0a, 0x0c, 0x4c, 0x6f,\n\t0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x12, 0x3c, 0x0a, 0x07, 0x41, 0x64,\n\t0x64, 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x16, 0x2e, 0x72, 0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x2e,\n\t0x6c, 0x62, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x1a, 0x17, 0x2e,\n\t0x72, 0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x2e, 0x6c, 0x62, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f,\n\t0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0a, 0x52, 0x65, 0x6d, 0x6f,\n\t0x76, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x19, 0x2e, 0x72, 0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74,\n\t0x2e, 0x6c, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65,\n\t0x71, 0x1a, 0x1a, 0x2e, 0x72, 0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x2e, 0x6c, 0x62, 0x2e, 0x52,\n\t0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12,\n\t0x45, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x19, 0x2e,\n\t0x72, 0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x2e, 0x6c, 0x62, 0x2e, 0x41, 0x64, 0x64, 0x42, 0x61,\n\t0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x1a, 0x2e, 0x72, 0x6f, 0x6c, 0x6c, 0x6f,\n\t0x75, 0x74, 0x2e, 0x6c, 0x62, 0x2e, 0x41, 0x64, 0x64, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64,\n\t0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65,\n\t0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x1c, 0x2e, 0x72, 0x6f, 0x6c, 0x6c, 0x6f, 0x75,\n\t0x74, 0x2e, 0x6c, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x65,\n\t0x6e, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x1d, 0x2e, 0x72, 0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x2e,\n\t0x6c, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64,\n\t0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0a, 0x50, 0x6f, 0x6f, 0x6c, 0x48, 0x65,\n\t0x61, 0x6c, 0x74, 0x68, 0x12, 0x19, 0x2e, 0x72, 0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x2e, 0x6c,\n\t0x62, 0x2e, 0x50, 0x6f, 0x6f, 0x6c, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x1a,\n\t0x1a, 0x2e, 0x72, 0x6f, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x2e, 0x6c, 0x62, 0x2e, 0x50, 0x6f, 0x6f,\n\t0x6c, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x42, 0x52, 0x5a,\n\t0x50, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x61, 0x63, 0x6b,\n\t0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x2f, 0x47, 0x6f, 0x2d, 0x66,\n\t0x6f, 0x72, 0x2d, 0x44, 0x65, 0x76, 0x4f, 0x70, 0x73, 0x2f, 0x63, 0x68, 0x61, 0x70, 0x74, 0x65,\n\t0x72, 0x2f, 0x31, 0x2f, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x36, 0x2f, 0x72, 0x6f,\n\t0x6c, 0x6c, 0x6f, 0x75, 0x74, 0x2f, 0x6c, 0x62, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6c,\n\t0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_lb_proto_rawDescOnce sync.Once\n\tfile_lb_proto_rawDescData = file_lb_proto_rawDesc\n)\n\nfunc file_lb_proto_rawDescGZIP() []byte {\n\tfile_lb_proto_rawDescOnce.Do(func() {\n\t\tfile_lb_proto_rawDescData = protoimpl.X.CompressGZIP(file_lb_proto_rawDescData)\n\t})\n\treturn file_lb_proto_rawDescData\n}\n\nvar file_lb_proto_enumTypes = make([]protoimpl.EnumInfo, 3)\nvar file_lb_proto_msgTypes = make([]protoimpl.MessageInfo, 17)\nvar file_lb_proto_goTypes = []interface{}{\n\t(PoolType)(0),             // 0: rollout.lb.PoolType\n\t(PoolStatus)(0),           // 1: rollout.lb.PoolStatus\n\t(BackendStatus)(0),        // 2: rollout.lb.BackendStatus\n\t(*HealthChecks)(nil),      // 3: rollout.lb.HealthChecks\n\t(*HealthCheck)(nil),       // 4: rollout.lb.HealthCheck\n\t(*StatusCheck)(nil),       // 5: rollout.lb.StatusCheck\n\t(*Backend)(nil),           // 6: rollout.lb.Backend\n\t(*IPBackend)(nil),         // 7: rollout.lb.IPBackend\n\t(*PoolHealth)(nil),        // 8: rollout.lb.PoolHealth\n\t(*BackendHealth)(nil),     // 9: rollout.lb.BackendHealth\n\t(*AddPoolReq)(nil),        // 10: rollout.lb.AddPoolReq\n\t(*AddPoolResp)(nil),       // 11: rollout.lb.AddPoolResp\n\t(*RemovePoolReq)(nil),     // 12: rollout.lb.RemovePoolReq\n\t(*RemovePoolResp)(nil),    // 13: rollout.lb.RemovePoolResp\n\t(*AddBackendReq)(nil),     // 14: rollout.lb.AddBackendReq\n\t(*AddBackendResp)(nil),    // 15: rollout.lb.AddBackendResp\n\t(*RemoveBackendReq)(nil),  // 16: rollout.lb.RemoveBackendReq\n\t(*RemoveBackendResp)(nil), // 17: rollout.lb.RemoveBackendResp\n\t(*PoolHealthReq)(nil),     // 18: rollout.lb.PoolHealthReq\n\t(*PoolHealthResp)(nil),    // 19: rollout.lb.PoolHealthResp\n}\nvar file_lb_proto_depIdxs = []int32{\n\t4,  // 0: rollout.lb.HealthChecks.health_checks:type_name -> rollout.lb.HealthCheck\n\t5,  // 1: rollout.lb.HealthCheck.status_check:type_name -> rollout.lb.StatusCheck\n\t7,  // 2: rollout.lb.Backend.ip_backend:type_name -> rollout.lb.IPBackend\n\t1,  // 3: rollout.lb.PoolHealth.status:type_name -> rollout.lb.PoolStatus\n\t9,  // 4: rollout.lb.PoolHealth.backends:type_name -> rollout.lb.BackendHealth\n\t6,  // 5: rollout.lb.BackendHealth.backend:type_name -> rollout.lb.Backend\n\t2,  // 6: rollout.lb.BackendHealth.status:type_name -> rollout.lb.BackendStatus\n\t0,  // 7: rollout.lb.AddPoolReq.pool_type:type_name -> rollout.lb.PoolType\n\t3,  // 8: rollout.lb.AddPoolReq.health_checks:type_name -> rollout.lb.HealthChecks\n\t6,  // 9: rollout.lb.AddBackendReq.backend:type_name -> rollout.lb.Backend\n\t6,  // 10: rollout.lb.RemoveBackendReq.backend:type_name -> rollout.lb.Backend\n\t8,  // 11: rollout.lb.PoolHealthResp.health:type_name -> rollout.lb.PoolHealth\n\t10, // 12: rollout.lb.LoadBalancer.AddPool:input_type -> rollout.lb.AddPoolReq\n\t12, // 13: rollout.lb.LoadBalancer.RemovePool:input_type -> rollout.lb.RemovePoolReq\n\t14, // 14: rollout.lb.LoadBalancer.AddBackend:input_type -> rollout.lb.AddBackendReq\n\t16, // 15: rollout.lb.LoadBalancer.RemoveBackend:input_type -> rollout.lb.RemoveBackendReq\n\t18, // 16: rollout.lb.LoadBalancer.PoolHealth:input_type -> rollout.lb.PoolHealthReq\n\t11, // 17: rollout.lb.LoadBalancer.AddPool:output_type -> rollout.lb.AddPoolResp\n\t13, // 18: rollout.lb.LoadBalancer.RemovePool:output_type -> rollout.lb.RemovePoolResp\n\t15, // 19: rollout.lb.LoadBalancer.AddBackend:output_type -> rollout.lb.AddBackendResp\n\t17, // 20: rollout.lb.LoadBalancer.RemoveBackend:output_type -> rollout.lb.RemoveBackendResp\n\t19, // 21: rollout.lb.LoadBalancer.PoolHealth:output_type -> rollout.lb.PoolHealthResp\n\t17, // [17:22] is the sub-list for method output_type\n\t12, // [12:17] is the sub-list for method input_type\n\t12, // [12:12] is the sub-list for extension type_name\n\t12, // [12:12] is the sub-list for extension extendee\n\t0,  // [0:12] is the sub-list for field type_name\n}\n\nfunc init() { file_lb_proto_init() }\nfunc file_lb_proto_init() {\n\tif File_lb_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_lb_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*HealthChecks); 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_lb_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*HealthCheck); 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_lb_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*StatusCheck); 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_lb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Backend); 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_lb_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*IPBackend); 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_lb_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*PoolHealth); 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_lb_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*BackendHealth); 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_lb_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*AddPoolReq); 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_lb_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*AddPoolResp); 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_lb_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*RemovePoolReq); 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_lb_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*RemovePoolResp); 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_lb_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*AddBackendReq); 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_lb_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*AddBackendResp); 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_lb_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*RemoveBackendReq); 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_lb_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*RemoveBackendResp); 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_lb_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*PoolHealthReq); 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_lb_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*PoolHealthResp); 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\tfile_lb_proto_msgTypes[1].OneofWrappers = []interface{}{\n\t\t(*HealthCheck_StatusCheck)(nil),\n\t}\n\tfile_lb_proto_msgTypes[3].OneofWrappers = []interface{}{\n\t\t(*Backend_IpBackend)(nil),\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_lb_proto_rawDesc,\n\t\t\tNumEnums:      3,\n\t\t\tNumMessages:   17,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_lb_proto_goTypes,\n\t\tDependencyIndexes: file_lb_proto_depIdxs,\n\t\tEnumInfos:         file_lb_proto_enumTypes,\n\t\tMessageInfos:      file_lb_proto_msgTypes,\n\t}.Build()\n\tFile_lb_proto = out.File\n\tfile_lb_proto_rawDesc = nil\n\tfile_lb_proto_goTypes = nil\n\tfile_lb_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "chapter/8/rollout/lb/proto/lb.proto",
    "content": "syntax = \"proto3\";\npackage rollout.lb;\n\noption go_package = \"github.com/PacktPublishing/Go-for-DevOps/chapter/6/rollout/lb/proto/lb\";\n\n// PoolType indicates what type of pool to use. This influences\n// how the pool distributes its workload.\nenum PoolType {\n\t// This indicates an error by the user.\n\tPT_UNKNOWN = 0;\n\t// The power of 2 choices selection pool.\n\tPT_P2C = 1;\n}\n\nenum PoolStatus {\n\t// This indicates an error by the developers.\n\tPS_UNKNOWN = 0;\n\t// The pool has all its configured backends working.\n\tPS_FULL = 1;\n\t// The pool has no configured backends.\n\tPS_EMPTY = 2;\n\t// The pool has one or more configured backends not working.\n\tPS_DEGRADED = 3;\n}\n\n// BackendStatus details the status of a backend.\nenum BackendStatus {\n\t// This indicates an error by the developers.\n\tBS_UNKNOWN = 0;\n\t// The node is healthy according to its health checks.\n\tBS_HEALTHY = 1;\n\t// The node is sick according to its health checks.\n\tBS_SICK = 2;\n}\n\nmessage HealthChecks {\n\trepeated HealthCheck health_checks = 1;\n\tint32 interval_secs = 2;\n}\n\nmessage HealthCheck {\n\toneof health_check {\n\t\tStatusCheck status_check = 1;\n\t}\n}\n\n// StatusCheck is a check against a URL path. That path must\n// emit in its body one of the healthy_values or it fails.\nmessage StatusCheck {\n\tstring url_path = 1;\n\trepeated string healthy_values = 2;\n}\n\nmessage Backend {\n\toneof backend {\n\t\tIPBackend ip_backend = 1;\n\t}\n}\n\n// IPBackend provides a Backend that has its endpoint as an ip:port.\nmessage IPBackend {\n\t// The IP(v4 or v6) to use as the host.\n\tstring ip = 1;\n\t// The port number to connect on.\n\tint32 port = 2;\n\t// The url_path to forward to. Generally this is empty.\n\tstring url_path = 3;\n}\n\n// PoolHealth details the health status of a pool.\nmessage PoolHealth {\n\t// The status of the pool.\n\tPoolStatus status = 1;\n\t// The pool's backend health status. This can contain all backends\n\t// or only sick/healthy backends, depending on the request.\n\trepeated BackendHealth backends = 2;\n}\n\n// BackendHealth details the health status of a backend.\nmessage BackendHealth {\n\tBackend backend = 1;\n\tBackendStatus status = 2;\n}\n\n\n// AddPoolReq requests to create a pool for handling requests.\nmessage AddPoolReq {\n\t// The URL pattern to direct traffic too.\n\tstring pattern = 1;\n\t// The type of traffic distribution pool to use.\n\tPoolType pool_type = 2;\n\t// Health checks to against backends.\n\tHealthChecks health_checks = 4;\n}\n\n// AddPoolResp is the response to adding a pool.\nmessage AddPoolResp {}\n\n// RemovePoolReq is used to remove a pool by its pattern.\nmessage RemovePoolReq {\n\t// Pattern is the pattern to remove.\n\tstring pattern = 1;\n}\n\n// RemovePoolResp is the response to removing a pool.\nmessage RemovePoolResp {}\n\n// AddBackendReq adds a backend to a pool.\nmessage AddBackendReq {\n\t// The pattern to add a backend to.\n\tstring pattern = 1;\n\t// The backend to add to the pool.\n\tBackend backend = 2;\n}\n\nmessage AddBackendResp {}\n\n// RemoveBackendReq is used to remove a Backend from a Pool.\nmessage RemoveBackendReq {\n\t// The pool pattern to remove the backend from.\n\tstring pattern = 1;\n\t// The backend to remove from the pool.\n\tBackend backend = 2;\n}\n\n// RemoveBackendResp is the response to removing a Backend.\nmessage RemoveBackendResp {}\n\n// PoolHealthReq is a request to get the health of a pool.\nmessage PoolHealthReq {\n\t// Pattern is the pool pattern you are getting health for.\n\tstring pattern = 1;\n\t// If set to true, will return the backends that are healthy.\n\tbool healthy = 3;\n\t// If set to true, will return backends that are sick.\n\tbool sick = 4;\n}\n\nmessage PoolHealthResp {\n\tPoolHealth health = 1;\n}\n\nservice LoadBalancer {\n\trpc AddPool(AddPoolReq) returns (AddPoolResp) {};\n\trpc RemovePool(RemovePoolReq) returns (RemovePoolResp) {};\n\trpc AddBackend(AddBackendReq) returns (AddBackendResp) {};\n\trpc RemoveBackend(RemoveBackendReq) returns (RemoveBackendResp) {};\n\trpc PoolHealth(PoolHealthReq) returns (PoolHealthResp) {};\n}\n"
  },
  {
    "path": "chapter/8/rollout/lb/proto/lb_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n\npackage lb\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// LoadBalancerClient is the client API for LoadBalancer 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 LoadBalancerClient interface {\n\tAddPool(ctx context.Context, in *AddPoolReq, opts ...grpc.CallOption) (*AddPoolResp, error)\n\tRemovePool(ctx context.Context, in *RemovePoolReq, opts ...grpc.CallOption) (*RemovePoolResp, error)\n\tAddBackend(ctx context.Context, in *AddBackendReq, opts ...grpc.CallOption) (*AddBackendResp, error)\n\tRemoveBackend(ctx context.Context, in *RemoveBackendReq, opts ...grpc.CallOption) (*RemoveBackendResp, error)\n\tPoolHealth(ctx context.Context, in *PoolHealthReq, opts ...grpc.CallOption) (*PoolHealthResp, error)\n}\n\ntype loadBalancerClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewLoadBalancerClient(cc grpc.ClientConnInterface) LoadBalancerClient {\n\treturn &loadBalancerClient{cc}\n}\n\nfunc (c *loadBalancerClient) AddPool(ctx context.Context, in *AddPoolReq, opts ...grpc.CallOption) (*AddPoolResp, error) {\n\tout := new(AddPoolResp)\n\terr := c.cc.Invoke(ctx, \"/rollout.lb.LoadBalancer/AddPool\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *loadBalancerClient) RemovePool(ctx context.Context, in *RemovePoolReq, opts ...grpc.CallOption) (*RemovePoolResp, error) {\n\tout := new(RemovePoolResp)\n\terr := c.cc.Invoke(ctx, \"/rollout.lb.LoadBalancer/RemovePool\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *loadBalancerClient) AddBackend(ctx context.Context, in *AddBackendReq, opts ...grpc.CallOption) (*AddBackendResp, error) {\n\tout := new(AddBackendResp)\n\terr := c.cc.Invoke(ctx, \"/rollout.lb.LoadBalancer/AddBackend\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *loadBalancerClient) RemoveBackend(ctx context.Context, in *RemoveBackendReq, opts ...grpc.CallOption) (*RemoveBackendResp, error) {\n\tout := new(RemoveBackendResp)\n\terr := c.cc.Invoke(ctx, \"/rollout.lb.LoadBalancer/RemoveBackend\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *loadBalancerClient) PoolHealth(ctx context.Context, in *PoolHealthReq, opts ...grpc.CallOption) (*PoolHealthResp, error) {\n\tout := new(PoolHealthResp)\n\terr := c.cc.Invoke(ctx, \"/rollout.lb.LoadBalancer/PoolHealth\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// LoadBalancerServer is the server API for LoadBalancer service.\n// All implementations must embed UnimplementedLoadBalancerServer\n// for forward compatibility\ntype LoadBalancerServer interface {\n\tAddPool(context.Context, *AddPoolReq) (*AddPoolResp, error)\n\tRemovePool(context.Context, *RemovePoolReq) (*RemovePoolResp, error)\n\tAddBackend(context.Context, *AddBackendReq) (*AddBackendResp, error)\n\tRemoveBackend(context.Context, *RemoveBackendReq) (*RemoveBackendResp, error)\n\tPoolHealth(context.Context, *PoolHealthReq) (*PoolHealthResp, error)\n\tmustEmbedUnimplementedLoadBalancerServer()\n}\n\n// UnimplementedLoadBalancerServer must be embedded to have forward compatible implementations.\ntype UnimplementedLoadBalancerServer struct {\n}\n\nfunc (UnimplementedLoadBalancerServer) AddPool(context.Context, *AddPoolReq) (*AddPoolResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method AddPool not implemented\")\n}\nfunc (UnimplementedLoadBalancerServer) RemovePool(context.Context, *RemovePoolReq) (*RemovePoolResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method RemovePool not implemented\")\n}\nfunc (UnimplementedLoadBalancerServer) AddBackend(context.Context, *AddBackendReq) (*AddBackendResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method AddBackend not implemented\")\n}\nfunc (UnimplementedLoadBalancerServer) RemoveBackend(context.Context, *RemoveBackendReq) (*RemoveBackendResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method RemoveBackend not implemented\")\n}\nfunc (UnimplementedLoadBalancerServer) PoolHealth(context.Context, *PoolHealthReq) (*PoolHealthResp, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method PoolHealth not implemented\")\n}\nfunc (UnimplementedLoadBalancerServer) mustEmbedUnimplementedLoadBalancerServer() {}\n\n// UnsafeLoadBalancerServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to LoadBalancerServer will\n// result in compilation errors.\ntype UnsafeLoadBalancerServer interface {\n\tmustEmbedUnimplementedLoadBalancerServer()\n}\n\nfunc RegisterLoadBalancerServer(s grpc.ServiceRegistrar, srv LoadBalancerServer) {\n\ts.RegisterService(&LoadBalancer_ServiceDesc, srv)\n}\n\nfunc _LoadBalancer_AddPool_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(AddPoolReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(LoadBalancerServer).AddPool(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/rollout.lb.LoadBalancer/AddPool\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(LoadBalancerServer).AddPool(ctx, req.(*AddPoolReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _LoadBalancer_RemovePool_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(RemovePoolReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(LoadBalancerServer).RemovePool(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/rollout.lb.LoadBalancer/RemovePool\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(LoadBalancerServer).RemovePool(ctx, req.(*RemovePoolReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _LoadBalancer_AddBackend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(AddBackendReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(LoadBalancerServer).AddBackend(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/rollout.lb.LoadBalancer/AddBackend\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(LoadBalancerServer).AddBackend(ctx, req.(*AddBackendReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _LoadBalancer_RemoveBackend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(RemoveBackendReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(LoadBalancerServer).RemoveBackend(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/rollout.lb.LoadBalancer/RemoveBackend\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(LoadBalancerServer).RemoveBackend(ctx, req.(*RemoveBackendReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _LoadBalancer_PoolHealth_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(PoolHealthReq)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(LoadBalancerServer).PoolHealth(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/rollout.lb.LoadBalancer/PoolHealth\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(LoadBalancerServer).PoolHealth(ctx, req.(*PoolHealthReq))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// LoadBalancer_ServiceDesc is the grpc.ServiceDesc for LoadBalancer 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 LoadBalancer_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"rollout.lb.LoadBalancer\",\n\tHandlerType: (*LoadBalancerServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"AddPool\",\n\t\t\tHandler:    _LoadBalancer_AddPool_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"RemovePool\",\n\t\t\tHandler:    _LoadBalancer_RemovePool_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"AddBackend\",\n\t\t\tHandler:    _LoadBalancer_AddBackend_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"RemoveBackend\",\n\t\t\tHandler:    _LoadBalancer_RemoveBackend_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"PoolHealth\",\n\t\t\tHandler:    _LoadBalancer_PoolHealth_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"lb.proto\",\n}\n"
  },
  {
    "path": "chapter/8/rollout/lb/sample/web/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\n\t\"github.com/google/uuid\"\n)\n\nvar (\n\tnode = flag.String(\"node\", uuid.New().String(), \"The node name\")\n\tport = flag.Int(\"port\", 8082, \"The port to run on\")\n)\n\nfunc main() {\n\tflag.Parse()\n\n\thttp.HandleFunc(\"/\", func(w http.ResponseWriter, r *http.Request) {\n\t\tfmt.Fprintf(w, \"Hello web from node \"+*node)\n\t})\n\thttp.HandleFunc(\"/healthz\", func(w http.ResponseWriter, r *http.Request) {\n\t\tfmt.Fprintf(w, \"ok\")\n\t})\n\tlog.Println(\"running on port: \", *port)\n\tlog.Fatal(http.ListenAndServe(fmt.Sprintf(\":%d\", *port), nil))\n}\n"
  },
  {
    "path": "chapter/8/rollout/lb/server/grpc/server.go",
    "content": "// Package grpc implements a gRPC server for controlling our HTTP load balancer.\npackage grpc\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"net\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/8/rollout/lb/server/http\"\n\n\t\"google.golang.org/grpc\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/8/rollout/lb/proto\"\n)\n\n// Server is a gRPC server for interacting with the load balancer.\ntype Server struct {\n\tpb.UnimplementedLoadBalancerServer\n\n\taddr       string\n\tlb         *http.LoadBalancer\n\tgrpcServer *grpc.Server\n\n\tmu sync.Mutex\n}\n\n// New creates a new instance of Server.\nfunc New(addr string, lb *http.LoadBalancer) (*Server, error) {\n\tvar opts []grpc.ServerOption\n\n\ts := &Server{\n\t\taddr:       addr,\n\t\tlb:         lb,\n\t\tgrpcServer: grpc.NewServer(opts...),\n\t}\n\ts.grpcServer.RegisterService(&pb.LoadBalancer_ServiceDesc, s)\n\n\treturn s, nil\n}\n\n// Start starts the server and blocks.\nfunc (s *Server) Start() error {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\n\tlis, err := net.Listen(\"tcp\", s.addr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn s.grpcServer.Serve(lis)\n}\n\n// Stop stops the server.\nfunc (s *Server) Stop() {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\n\ts.grpcServer.Stop()\n}\n\n// AddPool adds a pool as defined in req.\nfunc (s *Server) AddPool(ctx context.Context, req *pb.AddPoolReq) (*pb.AddPoolResp, error) {\n\tlog.Println(\"adding pool\")\n\tif strings.TrimSpace(req.Pattern) == \"\" {\n\t\treturn nil, fmt.Errorf(\"pattern must not be empty\")\n\t}\n\n\tif req.PoolType == pb.PoolType_PT_UNKNOWN {\n\t\treturn nil, fmt.Errorf(\"must set a pool_type\")\n\t}\n\n\tif len(req.HealthChecks.HealthChecks) == 0 {\n\t\treturn nil, fmt.Errorf(\"must have at least 1 health_check\")\n\t}\n\n\tvar hcs []http.HealthCheck\n\tfor _, hc := range req.HealthChecks.HealthChecks {\n\t\tswitch {\n\t\tcase hc.GetStatusCheck() != nil:\n\t\t\tscr := hc.GetStatusCheck()\n\t\t\tsc, err := http.StatusCheck(scr.UrlPath, scr.HealthyValues)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\thcs = append(hcs, sc)\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"a health_check is missing its concrete type\")\n\t\t}\n\t}\n\tinterval := time.Duration(req.HealthChecks.IntervalSecs) * time.Second\n\n\tvar pool http.Pool\n\n\tswitch req.PoolType {\n\tcase pb.PoolType_PT_P2C:\n\t\tvar err error\n\t\tpool, err = http.NewP2C(\n\t\t\thttp.HealthMultiplexer(hcs...),\n\t\t\tinterval,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown pool_type(%v\", req.PoolType)\n\t}\n\n\tif err := s.lb.AddPool(req.Pattern, pool); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &pb.AddPoolResp{}, nil\n}\n\n// RemovePool removes a pool as defined in req.\nfunc (s *Server) RemovePool(ctx context.Context, req *pb.RemovePoolReq) (*pb.RemovePoolResp, error) {\n\tif strings.TrimSpace(req.Pattern) == \"\" {\n\t\treturn nil, fmt.Errorf(\"pattern must not be empty\")\n\t}\n\tif err := s.lb.RemovePool(req.Pattern); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &pb.RemovePoolResp{}, nil\n}\n\n// AddBackend adds a backend as defined in req.\nfunc (s *Server) AddBackend(ctx context.Context, req *pb.AddBackendReq) (*pb.AddBackendResp, error) {\n\tlog.Println(\"adding backend\")\n\tif strings.TrimSpace(req.Pattern) == \"\" {\n\t\treturn nil, fmt.Errorf(\"pattern must not be empty\")\n\t}\n\tpool, err := s.lb.GetPool(req.Pattern)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar back http.Backend\n\n\tswitch {\n\tcase req.Backend.GetIpBackend() != nil:\n\t\tv := req.Backend.GetIpBackend()\n\t\tip := net.ParseIP(v.Ip)\n\t\tif ip == nil {\n\t\t\treturn nil, fmt.Errorf(\"backend ip is invalid\")\n\t\t}\n\t\tif v.Port < 1 || v.Port > 65534 {\n\t\t\treturn nil, fmt.Errorf(\"port is invalid\")\n\t\t}\n\t\tb, err := http.NewIPBackend(ip, v.Port, v.UrlPath)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tback = b\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"a backend is missing its concrete type\")\n\t}\n\tif err := pool.Add(ctx, back); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &pb.AddBackendResp{}, nil\n}\n\n// RemoveBackend remoes a backend as defined in req.\nfunc (s *Server) RemoveBackend(ctx context.Context, req *pb.RemoveBackendReq) (*pb.RemoveBackendResp, error) {\n\tif strings.TrimSpace(req.Pattern) == \"\" {\n\t\treturn nil, fmt.Errorf(\"pattern must not be empty\")\n\t}\n\n\tvar back http.Backend\n\n\tswitch {\n\tcase req.Backend.GetIpBackend() != nil:\n\t\tv := req.Backend.GetIpBackend()\n\t\tip := net.ParseIP(v.Ip)\n\t\tif ip == nil {\n\t\t\treturn nil, fmt.Errorf(\"backend ip is invalid\")\n\t\t}\n\t\tif v.Port < 1 || v.Port > 65534 {\n\t\t\treturn nil, fmt.Errorf(\"port is invalid\")\n\t\t}\n\t\tb, err := http.NewIPBackend(ip, v.Port, v.UrlPath)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tback = b\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"a backend is missing its concrete type\")\n\t}\n\n\tpool, err := s.lb.GetPool(req.Pattern)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := pool.Remove(ctx, back); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &pb.RemoveBackendResp{}, nil\n}\n\n// PoolHealth returns the health of a pool defined in req.\nfunc (s *Server) PoolHealth(ctx context.Context, req *pb.PoolHealthReq) (*pb.PoolHealthResp, error) {\n\tph, err := s.lb.PoolHealth(ctx, req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &pb.PoolHealthResp{Health: ph}, nil\n}\n"
  },
  {
    "path": "chapter/8/rollout/lb/server/http/p2c.go",
    "content": "/*\nThis is an implementation of the power of 2 choices selction method, made popular in this paper:\nhttps://www.eecs.harvard.edu/~michaelm/postscripts/mythesis.pdf\n\nThis paper is based on the work of:\nhttps://homes.cs.washington.edu/~karlin/papers/AzarBKU99.pdf\n*/\n\npackage http\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"math/rand\"\n\t\"net/http\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/8/rollout/lb/proto\"\n)\n\n// weightedBackend implements Backend with a wrapper around another Backend. This\n// implementation adds a weight that can be used to help determine which Backend to\n// choose when doing a P2C.\ntype weightedBackend struct {\n\tBackend\n\n\tweight int32\n}\n\nfunc (w *weightedBackend) get() int32 {\n\treturn atomic.LoadInt32(&w.weight)\n}\n\nfunc (w *weightedBackend) call() {\n\tw.Backend.call()\n\tatomic.AddInt32(&w.weight, 1)\n}\n\nfunc (w *weightedBackend) done() {\n\tw.Backend.done()\n\ti := atomic.AddInt32(&w.weight, -1)\n\tif i < 0 {\n\t\tpanic(\"weightedBackend cannot be < 0\")\n\t}\n}\n\nfunc (w *weightedBackend) handler() http.Handler {\n\treturn http.HandlerFunc(\n\t\tfunc(wr http.ResponseWriter, r *http.Request) {\n\t\t\tw.call()\n\t\t\tdefer w.done()\n\t\t\tw.Backend.handler().ServeHTTP(wr, r)\n\t\t},\n\t)\n}\n\n// P2C implements Pool using the Power of 2 choice selection method.\ntype P2C struct {\n\thc       HealthCheck\n\tinterval time.Duration\n\n\tmu            sync.Mutex\n\thealthy, sick *atomic.Value // []*weightedBackend\n\trand          *rand.Rand\n\n\tdone chan struct{}\n}\n\n// NewP2C creates a new P2C instance. hc is the health check\n// to perform on the backend to make sure its healthy and interval is how often to do\n// the health check.\nfunc NewP2C(hc HealthCheck, interval time.Duration) (*P2C, error) {\n\tsp := &P2C{\n\t\thc:       hc,\n\t\tinterval: interval,\n\t\thealthy:  &atomic.Value{},\n\t\tsick:     &atomic.Value{},\n\t\trand:     rand.New(rand.NewSource(time.Now().UnixNano())),\n\t\tdone:     make(chan struct{}),\n\t}\n\n\tsp.healthy.Store([]*weightedBackend{})\n\tsp.sick.Store([]*weightedBackend{})\n\tgo sp.healthLoop()\n\n\treturn sp, nil\n}\n\n// Close implements Pool.Close().\nfunc (s *P2C) Close() error {\n\tclose(s.done)\n\treturn nil\n}\n\n// Add implements Pool.Add().\nfunc (s *P2C) Add(ctx context.Context, b Backend) error {\n\tif ctx.Err() != nil {\n\t\treturn ctx.Err()\n\t}\n\n\tif err := s.hc(ctx, b.url().String()); err != nil {\n\t\tb.setHealth(sick)\n\t\treturn fmt.Errorf(\"backend is sick: %w\", err)\n\t}\n\tb.setHealth(healthy)\n\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\n\tif err := s.addToValue(&weightedBackend{Backend: b}, s.healthy); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// Remove implements Pool.Remove().\nfunc (s *P2C) Remove(ctx context.Context, b Backend) error {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\n\ts.removeFromValue(b, s.healthy)\n\ts.removeFromValue(b, s.sick)\n\treturn nil\n}\n\n// Health implements Pool.Health().\nfunc (s *P2C) Health(ctx context.Context, req *pb.PoolHealthReq) (*pb.PoolHealth, error) {\n\tstatus := pb.PoolStatus_PS_FULL\n\n\thealthy := s.healthy.Load().([]*weightedBackend)\n\tsick := s.sick.Load().([]*weightedBackend)\n\n\thealthyNodes := len(healthy)\n\tsickNodes := len(sick)\n\n\tif sickNodes == 0 && healthyNodes == 0 {\n\t\treturn &pb.PoolHealth{\n\t\t\tStatus: pb.PoolStatus_PS_EMPTY,\n\t\t}, nil\n\t}\n\n\tif sickNodes > 0 {\n\t\tstatus = pb.PoolStatus_PS_DEGRADED\n\t}\n\n\tph := &pb.PoolHealth{\n\t\tStatus: status,\n\t}\n\n\tif req.Healthy {\n\t\tfor _, wb := range healthy {\n\t\t\tswitch v := wb.Backend.(type) {\n\t\t\tcase *IPBackend:\n\t\t\t\th := &pb.BackendHealth{\n\t\t\t\t\tStatus: pb.BackendStatus_BS_HEALTHY,\n\t\t\t\t\tBackend: &pb.Backend{\n\t\t\t\t\t\tBackend: &pb.Backend_IpBackend{\n\t\t\t\t\t\t\tIpBackend: &pb.IPBackend{\n\t\t\t\t\t\t\t\tIp:      v.ip.String(),\n\t\t\t\t\t\t\t\tPort:    v.port,\n\t\t\t\t\t\t\t\tUrlPath: v.urlPath,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\tph.Backends = append(ph.Backends, h)\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"an unknown healthy backend type found(%T)\", wb.Backend)\n\t\t\t}\n\t\t}\n\t}\n\tif req.Sick {\n\t\tfor _, wb := range sick {\n\t\t\tswitch v := wb.Backend.(type) {\n\t\t\tcase *IPBackend:\n\t\t\t\th := &pb.BackendHealth{\n\t\t\t\t\tStatus: pb.BackendStatus_BS_SICK,\n\t\t\t\t\tBackend: &pb.Backend{\n\t\t\t\t\t\tBackend: &pb.Backend_IpBackend{\n\t\t\t\t\t\t\tIpBackend: &pb.IPBackend{\n\t\t\t\t\t\t\t\tIp:      v.ip.String(),\n\t\t\t\t\t\t\t\tPort:    v.port,\n\t\t\t\t\t\t\t\tUrlPath: v.urlPath,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\tph.Backends = append(ph.Backends, h)\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"an unknown sick backend type found(%T)\", wb.Backend)\n\t\t\t}\n\t\t}\n\t}\n\treturn ph, nil\n}\n\nfunc (s *P2C) addToValue(b *weightedBackend, v *atomic.Value) error {\n\tbacks := (*v).Load().([]*weightedBackend)\n\tn := make([]*weightedBackend, 0, len(backs)+1)\n\tfor _, back := range backs {\n\t\t// This is quite slow, but... we should not be adding backends often\n\t\t// to a single instance. If this somehow becomes a bottleneck, we can\n\t\t// always calculate some hash on Add() to do checks on.\n\t\tif b.url().String() == back.url().String() {\n\t\t\treturn fmt.Errorf(\"backend already exists\")\n\t\t}\n\t\tn = append(n, back)\n\t}\n\tn = append(n, b)\n\n\tv.Store(n)\n\treturn nil\n}\n\nfunc (s *P2C) removeFromValue(b Backend, v *atomic.Value) error {\n\tbacks := v.Load().([]*weightedBackend)\n\n\tnewCap := len(backs) - 1\n\tif newCap < 0 {\n\t\treturn nil // No way it exists\n\t}\n\n\tn := make([]*weightedBackend, 0, newCap)\n\tfor _, back := range backs {\n\t\tif b.url().String() == back.url().String() {\n\t\t\tcontinue\n\t\t}\n\t\tn = append(n, back)\n\t}\n\tif len(backs) == len(n) {\n\t\treturn fmt.Errorf(\"could not find backend(%s)\", b.url().String())\n\t}\n\n\tv.Store(n)\n\treturn nil\n}\n\n// ServeHTTP implements Pool.ServeHTTP().\nfunc (s *P2C) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tbacks := s.healthy.Load().([]*weightedBackend)\n\tif len(backs) == 0 {\n\t\thttp.Error(w, \"no backends available\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tx := s.rand.Int31n(int32(len(backs)))\n\ty := s.rand.Int31n(int32(len(backs)))\n\n\tif backs[x].weight < backs[y].weight {\n\t\tbacks[x].handler().ServeHTTP(w, r)\n\t\treturn\n\t}\n\tbacks[y].handler().ServeHTTP(w, r)\n}\n\nfunc (s *P2C) healthLoop() {\n\tfor {\n\t\tselect {\n\t\tcase <-s.done:\n\t\t\treturn\n\t\tcase <-time.After(s.interval):\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\t\t\ts.healthChecks(ctx)\n\t\t\tcancel()\n\t\t}\n\t}\n}\n\nfunc (s *P2C) healthChecks(ctx context.Context) {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\n\twg := sync.WaitGroup{}\n\tfor _, b := range s.healthy.Load().([]*weightedBackend) {\n\t\tb := b\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tif err := s.hc(ctx, b.url().String()); err != nil {\n\t\t\t\ts.healthyToSick(b)\n\t\t\t}\n\t\t}()\n\t}\n\tfor _, b := range s.sick.Load().([]*weightedBackend) {\n\t\tb := b\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tif err := s.hc(ctx, b.url().String()); err == nil {\n\t\t\t\ts.sickToHealthy(b)\n\t\t\t}\n\t\t}()\n\t}\n\twg.Wait()\n}\n\nfunc (s *P2C) healthyToSick(b *weightedBackend) {\n\tlog.Printf(\"backend %s became sick\", b.url())\n\tb.setHealth(sick)\n\tif err := s.removeFromValue(b, s.healthy); err != nil {\n\t\tlog.Println(err)\n\t\treturn\n\t}\n\tif err := s.addToValue(b, s.sick); err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc (s *P2C) sickToHealthy(b *weightedBackend) {\n\tlog.Printf(\"backend %s became healthy\", b.url())\n\tb.setHealth(healthy)\n\tif err := s.removeFromValue(b, s.sick); err != nil {\n\t\tlog.Println(err)\n\t\treturn\n\t}\n\tif err := s.addToValue(b, s.healthy); err != nil {\n\t\tpanic(err)\n\t}\n}\n"
  },
  {
    "path": "chapter/8/rollout/lb/server/http/server.go",
    "content": "package http\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"net/url\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/8/rollout/lb/proto\"\n)\n\ntype routeHandler struct {\n\tmuxStore atomic.Value // *http.ServeMux\n}\n\nfunc newRouteHandler(mux *http.ServeMux) *routeHandler {\n\tif mux == nil {\n\t\tpanic(\"mux cannot be nil\")\n\t}\n\tr := &routeHandler{}\n\tr.muxStore.Store(mux)\n\treturn r\n}\n\nfunc (r *routeHandler) mux() *http.ServeMux {\n\treturn r.muxStore.Load().(*http.ServeMux)\n}\n\nfunc (r *routeHandler) replace(mux *http.ServeMux) {\n\tif mux == nil {\n\t\treturn\n\t}\n\tr.muxStore.Store(mux)\n}\n\nfunc (r *routeHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {\n\tr.muxStore.Load().(http.Handler).ServeHTTP(w, req)\n}\n\nfunc newServ(handler *routeHandler) *http.Server {\n\treturn &http.Server{\n\t\tHandler:        handler,\n\t\tIdleTimeout:    5 * time.Second,\n\t\tMaxHeaderBytes: 1024 * 1000,\n\t}\n}\n\n// LoadBalancer is an HTTP reverse proxy load balancer.\ntype LoadBalancer struct {\n\tmu      sync.Mutex\n\tpools   map[string]Pool\n\thandler *routeHandler\n\tserv    *http.Server\n}\n\n// New creates a new LoadBalancer instance.\nfunc New() (*LoadBalancer, error) {\n\thandler := newRouteHandler(http.NewServeMux())\n\treturn &LoadBalancer{\n\t\tpools:   map[string]Pool{},\n\t\thandler: handler,\n\t\tserv:    newServ(handler),\n\t}, nil\n}\n\n// AddPool adds a pool of backends that serve the serveURL listed here. If a pattern\n// is added more than once, this will panic.\nfunc (l *LoadBalancer) AddPool(pattern string, pool Pool) error {\n\tl.mu.Lock()\n\tdefer l.mu.Unlock()\n\n\tif _, ok := l.pools[pattern]; ok {\n\t\treturn fmt.Errorf(\"pattern(%s) is already registered\", pattern)\n\t}\n\tl.pools[pattern] = pool\n\n\tl.handler.mux().Handle(pattern, pool)\n\treturn nil\n}\n\n// GetPool returns a pool by its pattern.\nfunc (l *LoadBalancer) GetPool(pattern string) (Pool, error) {\n\tl.mu.Lock()\n\tdefer l.mu.Unlock()\n\n\tp, ok := l.pools[pattern]\n\tif ok {\n\t\treturn p, nil\n\t}\n\treturn nil, fmt.Errorf(\"pool(%s) not found\", pattern)\n}\n\n// RemovePool removes a pool of backends that serve a pattern. If the pattern does not\n// exist, the error is still nil.\nfunc (l *LoadBalancer) RemovePool(pattern string) error {\n\tl.mu.Lock()\n\tdefer l.mu.Unlock()\n\n\tp, ok := l.pools[pattern]\n\tif !ok {\n\t\treturn nil\n\t}\n\tp.Close()\n\n\tdelete(l.pools, pattern)\n\n\tmux := http.NewServeMux()\n\tfor k, v := range l.pools {\n\t\tmux.Handle(k, v)\n\t}\n\n\tl.handler.replace(mux)\n\treturn nil\n}\n\n// PoolHealth returns the health of a pool as defined in the req.\nfunc (l *LoadBalancer) PoolHealth(ctx context.Context, req *pb.PoolHealthReq) (*pb.PoolHealth, error) {\n\tl.mu.Lock()\n\tp, ok := l.pools[req.Pattern]\n\tl.mu.Unlock()\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"pool not found\")\n\t}\n\treturn p.Health(ctx, req)\n}\n\n// Serve will serve HTTP traffic(non-TLS) on lis.\nfunc (l *LoadBalancer) Serve(lis net.Listener) error {\n\treturn l.serv.Serve(lis)\n}\n\n// ServeTLS will serve HTTPS traffic on lis. See http.Server.ServeTLS for more documentation.\nfunc (l *LoadBalancer) ServeTLS(lis net.Listener, certFile, keyFile string) error {\n\treturn l.serv.ServeTLS(lis, certFile, keyFile)\n}\n\n// Pool represents a set of backends that serve a URL.\ntype Pool interface {\n\t// Add adds a new Backend to the pool. The Backend must be healthy.\n\tAdd(ctx context.Context, b Backend) error\n\t// Remove removes a backend from the loadbalancer.\n\tRemove(ctx context.Context, b Backend) error\n\t// Health returns the health of a pool.\n\tHealth(ctx context.Context, req *pb.PoolHealthReq) (*pb.PoolHealth, error)\n\t// Close closes the pool. It should not be used after this.\n\tClose() error\n\t// ServeHTTP implements http.Handler.\n\tServeHTTP(w http.ResponseWriter, r *http.Request)\n}\n\n// HealthCheck provides a health check for a backend. The state passed is the\n// current state of the backend. If state is Healthy but fails the check, this\n// will cause the backend to be removed from service . If state is Sick and\n// passes the check, the backend will be returned to service.\ntype HealthCheck func(ctx context.Context, endpoint string) error\n\n// HealthMultiplexer allows combining multiple HealthCheck(s) together.\nfunc HealthMultiplexer(healthChecks ...HealthCheck) HealthCheck {\n\treturn func(ctx context.Context, endpoint string) error {\n\t\tctx, cancel := context.WithCancel(ctx)\n\t\tdefer cancel()\n\n\t\tresults := make(chan error, len(healthChecks))\n\t\tfor _, hc := range healthChecks {\n\t\t\thc := hc\n\t\t\tgo func() {\n\t\t\t\tresults <- hc(ctx, endpoint)\n\t\t\t}()\n\t\t}\n\t\tfor i := 0; i < len(healthChecks); i++ {\n\t\t\tresult := <-results\n\t\t\tif result != nil {\n\t\t\t\treturn result\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n}\n\n// StatusCheck returns a HealthCheck that checks the status\nfunc StatusCheck(urlPath string, healthyValues []string) (HealthCheck, error) {\n\tif len(healthyValues) == 0 {\n\t\treturn nil, fmt.Errorf(\"must provide at least one healthy value\")\n\t}\n\treturn func(ctx context.Context, endpoint string) error {\n\t\tu, err := url.Parse(endpoint)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tu.Scheme = \"http\"\n\t\tu.Path = urlPath\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)\n\t\tdefer cancel()\n\n\t\tc := &http.Client{}\n\t\treq, err := http.NewRequestWithContext(ctx, \"GET\", u.String(), nil)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresp, err := c.Do(req)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer resp.Body.Close()\n\n\t\tb, err := io.ReadAll(resp.Body)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tb = bytes.TrimSpace(b)\n\t\tfor _, v := range healthyValues {\n\t\t\tif len(v) != len(b) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif string(b) == v {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn fmt.Errorf(\"not healthy, got status(%s)\", b)\n\t}, nil\n}\n\n// healthState is the health state of a backend.\ntype healthState int8\n\nconst (\n\tunknownHS healthState = 0\n\thealthy   healthState = 1\n\tsick      healthState = 2\n)\n\ntype Backend interface {\n\t// url is the URL of the backend.\n\turl() *url.URL\n\t// setSick marks the node as sick.\n\tsetHealth(healthState)\n\t// health returns the current healthState\n\thealth() healthState\n\t// call is called before a backend is used.\n\tcall()\n\t// done is called after a backend is used.\n\tdone()\n\t// handler provides the backends http.Handler.\n\thandler() http.Handler\n}\n\n// IPBackend provides a backend to our proxy that will use ip:port as the backend.\n// This gives us static backends that isolate us from DNS changes or failures.\ntype IPBackend struct {\n\tip      net.IP\n\tport    int32\n\turlPath string\n\n\thealthState atomic.Value // HealthState\n\n\tendpoint string\n\tu        *url.URL\n\thandle   *httputil.ReverseProxy\n}\n\n// NewIPBackend is the constructor for IPBackend.\nfunc NewIPBackend(ip net.IP, port int32, urlPath string) (*IPBackend, error) {\n\ti := &IPBackend{\n\t\tip:       ip,\n\t\tport:     port,\n\t\tendpoint: fmt.Sprintf(\"%s:%d\", ip, port),\n\t}\n\ti.healthState.Store(unknownHS)\n\ti.resolveURL()\n\ti.handle = httputil.NewSingleHostReverseProxy(i.u)\n\n\tif err := i.validate(); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn i, nil\n}\n\nfunc (i *IPBackend) validate() error {\n\tif i.ip.To4() == nil && i.ip.To16() == nil {\n\t\treturn fmt.Errorf(\"ip %q was not valid\", i.ip)\n\t}\n\tif i.port < 1 || i.port > 65534 {\n\t\treturn fmt.Errorf(\"port %d was not valid\", i.port)\n\t}\n\treturn nil\n}\n\nfunc (i *IPBackend) url() *url.URL {\n\treturn i.u\n}\n\nfunc (i *IPBackend) setHealth(hs healthState) {\n\ti.healthState.Store(hs)\n}\n\nfunc (i *IPBackend) health() healthState {\n\treturn i.healthState.Load().(healthState)\n}\n\nfunc (i *IPBackend) call() {} // not needed\nfunc (i *IPBackend) done() {} // not needed\n\nfunc (i *IPBackend) handler() http.Handler {\n\treturn i.handle\n}\n\nfunc (i *IPBackend) resolveURL() error {\n\tu, err := url.Parse(i.urlPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbase, err := url.Parse(\"http://\" + i.endpoint)\n\tif err != nil {\n\t\treturn err\n\t}\n\ti.u = base.ResolveReference(u)\n\treturn nil\n}\n"
  },
  {
    "path": "chapter/8/rollout/rollout.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/8/rollout/lb/client\"\n\n\t\"github.com/fatih/color\"\n\t\"github.com/rodaine/table\"\n\t\"golang.org/x/crypto/ssh\"\n\t\"golang.org/x/crypto/ssh/agent\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/8/rollout/lb/proto\"\n)\n\nvar (\n\tkeyFile = flag.String(\"keyFile\", \"\", \"The key file to use for SSH connections. If not set, uses the SSH agent.\")\n)\n\nvar (\n\theaderFmt = color.New(color.FgGreen, color.Underline).SprintfFunc()\n\tcolumnFmt = color.New(color.FgYellow).SprintfFunc()\n)\n\nfunc main() {\n\tflag.Parse()\n\n\tctx := context.Background()\n\n\tconfig, wf, err := setup()\n\tif err != nil {\n\t\tcolor.Red(\"Setup Error: %s\", err)\n\t\tos.Exit(1)\n\t}\n\n\terr = getSSHConfig(config)\n\tif err != nil {\n\t\tcolor.Red(\"SSH setup error: %s\", err)\n\t\tos.Exit(1)\n\t}\n\n\t// If the load balancer doesn't have pool \"/\", set one up.\n\tif _, err := wf.lb.PoolHealth(ctx, \"/\", false, false); err != nil {\n\t\terr := wf.lb.AddPool(\n\t\t\tctx,\n\t\t\t\"/\",\n\t\t\tpb.PoolType_PT_P2C,\n\t\t\tclient.HealthChecks{\n\t\t\t\tHealthChecks: []client.HealthCheck{\n\t\t\t\t\tclient.StatusCheck{\n\t\t\t\t\t\tURLPath:       \"/healthz\",\n\t\t\t\t\t\tHealthyValues: []string{\"ok\", \"OK\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tInterval: 5 * time.Second,\n\t\t\t},\n\t\t)\n\t\tif err != nil {\n\t\t\tcolor.Red(\"LB did not have pool `/` and couldn't create it: %s\", err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tcolor.Blue(\"Setup LB with pool `/`\")\n\t}\n\n\tcolor.Red(\"Starting Workflow\")\n\tif err := wf.run(ctx); err != nil {\n\t\tstatus := wf.status()\n\t\tcolor.Red(\"Workflow Failed: %s\", status.endState)\n\n\t\tvar tbl table.Table\n\t\tif status.endState == esPreconditionFailure {\n\t\t\ttbl = table.New(\"Failed State\", \"Error\")\n\t\t\ttbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt)\n\t\t\ttbl.AddRow(\"Precondition\", err)\n\t\t} else {\n\t\t\ttbl = table.New(\"Endpoint\", \"Failed State\", \"Error\")\n\t\t\ttbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt)\n\t\t\tfor _, action := range status.failures {\n\t\t\t\ttbl.AddRow(action.endpoint, action.failure(), action.err)\n\t\t\t}\n\t\t}\n\t\ttbl.Print()\n\t\tos.Exit(1)\n\t}\n\n\tstatus := wf.status()\n\tif len(status.failures) == 0 {\n\t\tcolor.Blue(\"Workflow Completed with no failures\")\n\t\tos.Exit(0)\n\t}\n\n\tcolor.Blue(\"Workflow Completed, but had %d failed actions\", len(status.failures))\n\tfor i := 0; i < 3; i++ {\n\t\tcolor.Green(\"Retrying failed actions in 5 minutes...\")\n\t\ttime.Sleep(5 * time.Minute)\n\t\tfmt.Println(\"Executing failed actions...\")\n\n\t\twf.retryFailed(ctx)\n\t\tstatus = wf.status()\n\t\tif len(status.failures) == 0 {\n\t\t\tbreak\n\t\t}\n\t\tcolor.Blue(\"Workflow Failures retry, but had %d failed actions\", len(status.failures))\n\t}\n\tstatus = wf.status()\n\tif len(status.failures) == 0 {\n\t\tcolor.Blue(\"Workflow Completed with no failures\")\n\t\tos.Exit(0)\n\t}\n\n\tcolor.Blue(\"Workflow Completed but with %d failures after retries exhausted\")\n\ttbl := table.New(\"Endpoint\", \"Failed State\", \"Error\")\n\ttbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt)\n\n\tfor _, action := range status.failures {\n\t\tfailure := action.failure()\n\t\tif failure == \"\" {\n\t\t\tfailure = \"during setup\"\n\t\t}\n\t\ttbl.AddRow(action.endpoint, failure, action.err)\n\t}\n\ttbl.Print()\n\tos.Exit(0)\n}\n\nfunc setup() (*config, *workflow, error) {\n\tif len(flag.Args()) != 1 {\n\t\treturn nil, nil, fmt.Errorf(\"must have argument to service file\")\n\t}\n\n\tb, err := os.ReadFile(flag.Args()[0])\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"can't open workflow configuration file: %w\", err)\n\t}\n\n\tconfig := &config{}\n\tif err := json.Unmarshal(b, config); err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"%q is misconfigured: %w\", flag.Args()[0], err)\n\t}\n\tif err := config.validate(); err != nil {\n\t\tlog.Println(string(b))\n\t\treturn nil, nil, fmt.Errorf(\"config file didn't validate: %w\", err)\n\t}\n\n\tlb, err := client.New(config.LB)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"can't connected to LB(%s): %s\\n\", config.LB, err)\n\t}\n\twf, err := newWorkflow(config, lb)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"could not create workflow: %w\", err)\n\t}\n\treturn config, wf, nil\n}\n\nfunc getSSHConfig(config *config) error {\n\tauth, err := getAuthFromFlags()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif config.BackendUser == \"\" {\n\t\tconfig.BackendUser = os.Getenv(\"USER\")\n\t}\n\tconfig.ssh = &ssh.ClientConfig{\n\t\tUser:            config.BackendUser,\n\t\tAuth:            []ssh.AuthMethod{auth},\n\t\tTimeout:         5 * time.Second,\n\t\tHostKeyCallback: ssh.InsecureIgnoreHostKey(),\n\t}\n\treturn nil\n}\n\nfunc getAuthFromFlags() (ssh.AuthMethod, error) {\n\tif *keyFile != \"\" {\n\t\treturn publicKey(*keyFile)\n\t}\n\treturn agentAuth()\n}\n\nfunc agentAuth() (ssh.AuthMethod, error) {\n\tconn, err := net.Dial(\"unix\", os.Getenv(\"SSH_AUTH_SOCK\"))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem dialing SSH agent when --key was not provided: %w\", err)\n\t}\n\n\tclient := agent.NewClient(conn)\n\treturn ssh.PublicKeysCallback(client.Signers), nil\n}\n\nfunc publicKey(privateKeyFile string) (ssh.AuthMethod, error) {\n\tk, err := os.ReadFile(privateKeyFile)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsigner, err := ssh.ParsePrivateKey(k)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn ssh.PublicKeys(signer), nil\n}\n"
  },
  {
    "path": "chapter/8/rollout/service.json",
    "content": "{\n    \"Concurrency\": 2,\n    \"CanaryNum\": 1,\n    \"MaxFailures\": 2,\n    \"Src\": \"/home/[user]/rollout/webserver\",\n    \"Dst\": \"/home/[user]/webserver\",\n    \"LB\": \"10.0.0.4:8081\",\n    \"Pattern\": \"/\",\n    \"Backends\": [\n            \"10.0.0.5\",\n            \"10.0.0.6\",\n            \"10.0.0.7\",\n            \"10.0.0.8\",\n            \"10.0.0.9\"\n    ],\n    \"BackendUser\": \"azureuser\",\n    \"BinaryPort\": 8082\n}"
  },
  {
    "path": "chapter/8/rollout/workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/PacktPublishing/Go-for-DevOps/chapter/8/rollout/lb/client\"\n\n\t\"github.com/fatih/color\"\n\t\"github.com/pkg/sftp\"\n\t\"golang.org/x/crypto/ssh\"\n\n\tpb \"github.com/PacktPublishing/Go-for-DevOps/chapter/8/rollout/lb/proto\"\n)\n\n//go:generate stringer -type=endState\n// endStates are the final states after a run of a workflow.\ntype endState int8\n\nconst (\n\t// esUnknown indicates we haven't reached and end state.\n\tesUnknown endState = 0\n\t// esSuccess means that the workflow has completed successfully. This\n\t// does not mean there haven't been failurea.\n\tesSuccess endState = 1\n\t// esPreconditionFailure means no work was done as we failed on a precondition.\n\tesPreconditionFailure endState = 2\n\t// esCanaryFailure indicates one of the canaries failed, stopping the workflow.\n\tesCanaryFailure endState = 3\n\t// esMaxFailures indicates that the workflow passed the canary phase, but failed\n\t// at a later phase.\n\tesMaxFailures endState = 4\n)\n\n// workflow represents our rollout workflow.\ntype workflow struct {\n\tconfig *config\n\tlb     *client.Client\n\n\tfailures int32\n\tendState endState\n\n\tactions []*actions\n}\n\n// newWorkflow creates a new workflow.\nfunc newWorkflow(config *config, lb *client.Client) (*workflow, error) {\n\twf := &workflow{\n\t\tconfig: config,\n\t\tlb:     lb,\n\t}\n\tif err := wf.buildActions(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn wf, nil\n}\n\n// run runs our workflow on the supplied \"actions\" doing \"canaryNum\" canaries,\n// then running \"concurrency\" number of actions that will stop at \"maxFailures\" number of\n// failurea.\nfunc (w *workflow) run(ctx context.Context) error {\n\t// Run a local precondition to make sure our load balancer is in a healthy state.\n\tpreCtx, cancel := context.WithTimeout(ctx, 30*time.Second)\n\tif err := w.checkLBState(preCtx); err != nil {\n\t\tw.endState = esPreconditionFailure\n\t\treturn fmt.Errorf(\"checkLBState precondition fail: %s\", err)\n\t}\n\tcancel()\n\n\t// Run our canaries one at a time. Any problem stops the workflow.\n\tfor i := 0; i < len(w.actions) && int32(i) < w.config.CanaryNum; i++ {\n\t\tcolor.Green(\"Running canary on: %s\", w.actions[i].endpoint)\n\t\tctx, cancel := context.WithTimeout(ctx, 10*time.Minute)\n\t\terr := w.actions[i].run(ctx)\n\t\tcancel()\n\t\tif err != nil {\n\t\t\tw.endState = esCanaryFailure\n\t\t\treturn fmt.Errorf(\"canary failure on endpoint(%s): %w\\n\", w.actions[i].endpoint, err)\n\t\t}\n\t\tcolor.Yellow(\"Sleeping after canary for 1 minutes\")\n\t\ttime.Sleep(1 * time.Minute)\n\t}\n\n\tlimit := make(chan struct{}, w.config.Concurrency)\n\twg := sync.WaitGroup{}\n\n\t// Run the rest of the actions, with a limit to our concurrency.\n\tfor i := w.config.CanaryNum; int(i) < len(w.actions); i++ {\n\t\ti := i\n\t\tlimit <- struct{}{}\n\t\tif atomic.LoadInt32(&w.failures) > w.config.MaxFailures {\n\t\t\tbreak\n\t\t}\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer func() { <-limit }()\n\t\t\tdefer wg.Done()\n\t\t\tctx, cancel := context.WithTimeout(ctx, 10*time.Minute)\n\n\t\t\tcolor.Green(\"Upgrading endpoint: %s\", w.actions[i].endpoint)\n\t\t\terr := w.actions[i].run(ctx)\n\t\t\tcancel()\n\t\t\tif err != nil {\n\t\t\t\tcolor.Red(\"Endpoint(%s) had upgrade error: %s\", w.actions[i].endpoint, err)\n\t\t\t\tatomic.AddInt32(&w.failures, 1)\n\t\t\t}\n\t\t}()\n\t}\n\twg.Wait()\n\n\tif atomic.LoadInt32(&w.failures) > w.config.MaxFailures {\n\t\tw.endState = esMaxFailures\n\t\treturn errors.New(\"exceeded max failures\")\n\t}\n\tw.endState = esSuccess\n\treturn nil\n}\n\n// retryFailed retries all failed actiona. This is only used if\nfunc (w *workflow) retryFailed(ctx context.Context) {\n\tif w.endState != esSuccess {\n\t\tpanic(\"retrlyFailed cannot be called unless the workflow was a success\")\n\t}\n\n\tws := w.status()\n\n\twg := sync.WaitGroup{}\n\n\tfor i := 0; i < len(ws.failures); i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tctx, cancel := context.WithTimeout(ctx, 10*time.Minute)\n\n\t\t\terr := ws.failures[i].run(ctx)\n\t\t\tcancel()\n\t\t\tif err == nil {\n\t\t\t\tatomic.AddInt32(&w.failures, -1)\n\t\t\t}\n\t\t}()\n\t}\n\twg.Wait()\n}\n\n// checkLBState checks the load balancer pool for \"pattern\" contains all \"endpoints\"\n// in a healthy state.\nfunc (w *workflow) checkLBState(ctx context.Context) error {\n\tph, err := w.lb.PoolHealth(ctx, w.config.Pattern, true, true)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"PoolHealth(%s) error: %w\", w.config.Pattern, err)\n\t}\n\tswitch ph.Status {\n\tcase pb.PoolStatus_PS_EMPTY:\n\tcase pb.PoolStatus_PS_FULL:\n\t\tif len(w.config.Backends) != len(ph.Backends) {\n\t\t\treturn fmt.Errorf(\"expected backends(%d) != found backends(%d)\", len(w.config.Backends), len(ph.Backends))\n\t\t}\n\t\tm := map[string]bool{}\n\t\tfor _, e := range w.config.Backends {\n\t\t\tm[e] = true\n\t\t}\n\t\tfor _, hb := range ph.Backends {\n\t\t\tswitch {\n\t\t\tcase hb.Backend.GetIpBackend() != nil:\n\t\t\t\tb := hb.Backend.GetIpBackend()\n\t\t\t\tif !m[b.Ip] {\n\t\t\t\t\treturn fmt.Errorf(\"configured backend %q not in config file\", b.Ip)\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"we only support IPBackend, got %T\", hb.Backend)\n\t\t\t}\n\t\t}\n\tdefault:\n\t\treturn fmt.Errorf(\"pool was not at full health, was %s\", ph.Status)\n\t}\n\treturn nil\n}\n\n// buildActions builds actions from our configuration file.\nfunc (w *workflow) buildActions() error {\n\tfor _, b := range w.config.Backends {\n\t\ta, err := newServerActions(b, w.config, w.lb)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tw.actions = append(w.actions, a)\n\t}\n\treturn nil\n}\n\ntype workflowStatus struct {\n\t// endState is the endState of the workflow.\n\tendState endState\n\t// failures is a list of failed actiona.\n\tfailures []*actions\n}\n\n// status will return the workflow's status after run() has complete.d\nfunc (w *workflow) status() workflowStatus {\n\tws := workflowStatus{endState: w.endState}\n\tfor _, action := range w.actions {\n\t\tif action.err != nil {\n\t\t\tws.failures = append(ws.failures, action)\n\t\t}\n\t}\n\treturn ws\n}\n\ntype stateFn func(ctx context.Context) (stateFn, error)\n\ntype actions struct {\n\tendpoint  string\n\tbackend   client.IPBackend\n\tconfig    *config\n\tsrcf      *os.File\n\tdst       string\n\tlb        *client.Client\n\tsshClient *ssh.Client\n\n\tstarted     bool\n\tfailedState stateFn\n\terr         error\n}\n\nfunc newServerActions(endpoint string, config *config, lb *client.Client) (*actions, error) {\n\tip, err := checkIP(endpoint)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &actions{\n\t\tendpoint: endpoint,\n\t\tbackend:  client.IPBackend{IP: ip, Port: int32(config.BinaryPort)},\n\t\tconfig:   config,\n\t\tlb:       lb,\n\t}, nil\n}\n\nfunc (a *actions) run(ctx context.Context) (err error) {\n\ta.srcf, err = os.Open(a.config.Src)\n\tif err != nil {\n\t\ta.err = fmt.Errorf(\"cannot open binary to copy(%s): %w\", a.config.Src, err)\n\t\treturn a.err\n\t}\n\n\tback := a.endpoint + \":22\"\n\ta.sshClient, err = ssh.Dial(\"tcp\", back, a.config.ssh)\n\tif err != nil {\n\t\ta.err = fmt.Errorf(\"problem dialing the endpoint(%s): %w\", back, err)\n\t\treturn a.err\n\t}\n\tdefer a.sshClient.Close()\n\n\tfn := a.rmBackend\n\tif a.failedState != nil {\n\t\tfn = a.failedState\n\t}\n\n\ta.started = true\n\tfor {\n\t\tif ctx.Err() != nil {\n\t\t\ta.err = ctx.Err()\n\t\t\treturn ctx.Err()\n\t\t}\n\t\tfn, err = fn(ctx)\n\t\tif err != nil {\n\t\t\ta.failedState = fn\n\t\t\ta.err = err\n\t\t\treturn err\n\t\t}\n\t\tif fn == nil {\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\nfunc (a *actions) rmBackend(ctx context.Context) (stateFn, error) {\n\terr := a.lb.RemoveBackend(ctx, a.config.Pattern, a.backend)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem removing backend from pool: %w\", err)\n\t}\n\n\treturn a.jobKill, nil\n}\n\nfunc (a *actions) jobKill(ctx context.Context) (stateFn, error) {\n\tpids, err := a.findPIDs(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem finding existing PIDs: %w\", err)\n\t}\n\n\tif len(pids) == 0 {\n\t\treturn a.cp, nil\n\t}\n\n\tif err := a.killPIDs(ctx, pids, 15); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to kill existing PIDs: %w\", err)\n\t}\n\n\tif err := a.waitForDeath(ctx, pids, 30*time.Second); err != nil {\n\t\tif err := a.killPIDs(ctx, pids, 9); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to kill existing PIDs: %w\", err)\n\t\t}\n\t\tif err := a.waitForDeath(ctx, pids, 10*time.Second); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to kill existing PIDs after -9: %w\", err)\n\t\t}\n\t\treturn a.cp, nil\n\t}\n\treturn a.cp, nil\n}\n\nfunc (a *actions) cp(ctx context.Context) (stateFn, error) {\n\tif err := a.sftp(); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to cp binary to remote end: %w\", err)\n\t}\n\treturn a.jobStart, nil\n}\n\nfunc (a *actions) jobStart(ctx context.Context) (stateFn, error) {\n\tif err := a.runBinary(ctx); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to start binary after copy: %w\", err)\n\t}\n\treturn a.reachable(ctx)\n}\n\nfunc (a *actions) reachable(ctx context.Context) (stateFn, error) {\n\tctx, cancel := context.WithTimeout(ctx, 1*time.Minute)\n\tdefer cancel()\n\n\tc := &http.Client{}\n\n\tu := &url.URL{\n\t\tHost:   net.JoinHostPort(a.endpoint, strconv.Itoa(a.config.BinaryPort)),\n\t\tPath:   \"/healthz\",\n\t\tScheme: \"http\",\n\t}\n\n\treq, err := http.NewRequestWithContext(\n\t\tctx,\n\t\t\"GET\",\n\t\tu.String(),\n\t\tnil,\n\t)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"problem creating HTTP request: %w\", err)\n\t}\n\n\tfor {\n\t\tif ctx.Err() != nil {\n\t\t\treturn nil, errors.New(\"reachable() timed out\")\n\t\t}\n\n\t\tresp, err := c.Do(req)\n\t\tif err != nil {\n\t\t\ttime.Sleep(1 * time.Second)\n\t\t\tcontinue\n\t\t}\n\t\tb, err := io.ReadAll(resp.Body)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif strings.TrimSpace(string(b)) == \"ok\" {\n\t\t\treturn a.addBackend, nil\n\t\t}\n\t}\n}\n\nfunc (a *actions) addBackend(ctx context.Context) (stateFn, error) {\n\terr := a.lb.AddBackend(ctx, a.config.Pattern, a.backend)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn nil, nil\n}\n\nfunc (a *actions) findPIDs(ctx context.Context) ([]string, error) {\n\tserviceName := path.Base(a.config.Src)\n\n\tresult, err := a.combinedOutput(\n\t\tctx,\n\t\ta.sshClient,\n\t\tfmt.Sprintf(\"pidof %s\", serviceName),\n\t)\n\tif err != nil {\n\t\tif err.(*ssh.ExitError).ExitStatus() == 127 {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn nil, nil\n\t}\n\n\treturn strings.Split(strings.TrimSpace(result), \" \"), nil\n}\n\nfunc (a *actions) killPIDs(ctx context.Context, pids []string, signal syscall.Signal) error {\n\tfor _, pid := range pids {\n\t\t_, err := a.combinedOutput(\n\t\t\tctx,\n\t\t\ta.sshClient,\n\t\t\tfmt.Sprintf(\"kill -s %d %s\", signal, pid),\n\t\t)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (a *actions) waitForDeath(ctx context.Context, pids []string, timeout time.Duration) error {\n\tt := time.NewTimer(timeout)\n\tdefer t.Stop()\n\tfor {\n\t\tselect {\n\t\tcase <-t.C:\n\t\t\treturn errors.New(\"timeout waiting for pids death\")\n\t\tdefault:\n\t\t}\n\n\t\tresults, err := a.findPIDs(ctx)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"findPIDs giving errors: %w\", err)\n\t\t}\n\n\t\tif len(results) == 0 {\n\t\t\treturn nil\n\t\t}\n\t\ttime.Sleep(1 * time.Second)\n\t}\n}\n\nfunc (a *actions) runBinary(ctx context.Context) error {\n\terr := a.startOnly(\n\t\tctx,\n\t\ta.sshClient,\n\t\tfmt.Sprintf(\"/usr/bin/nohup %s &\", a.config.Dst),\n\t)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"problem running the binary on the remove side: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc (a *actions) sftp() error {\n\tc, err := sftp.NewClient(a.sshClient)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not create SFTP client: %w\", err)\n\t}\n\tdefer c.Close()\n\n\tdstf, err := c.OpenFile(a.config.Dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"SFTP could not open file on remote destination(%s): %w\", a.config.Dst, err)\n\t}\n\tdefer dstf.Close()\n\tif err := dstf.Chmod(0770); err != nil {\n\t\treturn fmt.Errorf(\"SFTP could not set the file mode to 0770: %w\", err)\n\t}\n\n\t_, err = io.Copy(dstf, a.srcf)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"SFTP failed to do a complete copy: %w\", err)\n\t}\n\treturn nil\n}\n\n// combinedOutput runs a command on an SSH client. The context can be cancelled, however\n// SSH does not always honor the kill signals we send, so this might not break. So closing\n// the session does nothing. So depending on what the server is doing, cancelling the context\n// may do nothing and it may still block.\nfunc (*actions) combinedOutput(ctx context.Context, conn *ssh.Client, cmd string) (string, error) {\n\tsess, err := conn.NewSession()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer sess.Close()\n\n\tif v, ok := ctx.Deadline(); ok {\n\t\tt := time.NewTimer(v.Sub(time.Now()))\n\t\tdefer t.Stop()\n\n\t\tgo func() {\n\t\t\tx := <-t.C\n\t\t\tif !x.IsZero() {\n\t\t\t\tsess.Signal(ssh.SIGKILL)\n\t\t\t}\n\t\t}()\n\t}\n\n\tb, err := sess.Output(cmd)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(b), nil\n}\n\nfunc (*actions) startOnly(ctx context.Context, conn *ssh.Client, cmd string) error {\n\tsess, err := conn.NewSession()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not start new SSH session: %w\", err)\n\t}\n\t// Note: don't close the session, it will prevent the program from starting.\n\n\treturn sess.Start(cmd)\n}\n\nfunc (a *actions) failure() string {\n\tif a.failedState == nil {\n\t\treturn \"\"\n\t}\n\n\treturn runtime.FuncForPC(reflect.ValueOf(a.failedState).Pointer()).Name()\n}\n"
  },
  {
    "path": "chapter/8/scanner/scanner.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\t\"os/exec\"\n\t\"os/user\"\n\t\"sync\"\n\t\"time\"\n\n\t\"inet.af/netaddr\"\n)\n\nconst (\n\tping = \"ping\"\n\tssh  = \"ssh\"\n\n\tuname = \"uname\"\n)\n\nfunc main() {\n\t_, err := exec.LookPath(ping)\n\tif err != nil {\n\t\tlog.Fatal(\"cannot find ping in our PATH\")\n\t}\n\t_, err = exec.LookPath(ssh)\n\tif err != nil {\n\t\tlog.Fatal(\"cannot find ssh in our PATH\")\n\t}\n\n\tif len(os.Args) != 2 {\n\t\tlog.Fatal(\"error: only one argument allowed, the network CIDR to scan\")\n\t}\n\n\tipCh, err := hosts(os.Args[1])\n\tif err != nil {\n\t\tlog.Fatalf(\"error: CIDR address did not parse: %s\", err)\n\t}\n\n\tu, err := user.Current()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tscanResults := scanPrefixes(ipCh)\n\tunameResults := unamePrefixes(u.Username, scanResults)\n\n\tfor rec := range unameResults {\n\t\tb, _ := json.Marshal(rec)\n\t\tfmt.Printf(\"%s\\n\", b)\n\t}\n}\n\n// record holds information about a scan of a host.\ntype record struct {\n\t// Host is the IP address of the host.\n\tHost net.IP\n\t// Reachable indicates if this host was pingable.\n\tReachable bool\n\t// LoginSSH indicates if we were able to authenticate with SSH.\n\tLoginSSH bool\n\t// Uname is the output of the \"uname -a\" command. If this is an empty string\n\t// but LoginSSH is true, this means uname was not supported by the host.\n\tUname string\n}\n\n// host takes a CIDR string (192.168.0.0/24) and returns all host IPs for that network.\n// This will not send back the broadcast or network addresses. Does not support /31 addresses.\nfunc hosts(cidr string) (chan net.IP, error) {\n\tch := make(chan net.IP, 1)\n\n\tprefix, err := netaddr.ParseIPPrefix(cidr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tgo func() {\n\t\tdefer close(ch)\n\n\t\tvar last net.IP\n\t\tfor ip := prefix.IP().Next(); prefix.Contains(ip); ip = ip.Next() {\n\t\t\t// Prevents sending the broadcast address.\n\t\t\tif len(last) != 0 {\n\t\t\t\t//log.Printf(\"sending: %s, contained: %v\", last, prefix.Contains(ip))\n\t\t\t\tch <- last\n\t\t\t}\n\t\t\tlast = ip.IPAddr().IP\n\t\t}\n\t}()\n\treturn ch, nil\n}\n\n// scanPrefixes takes a channel of net.IP and pings them. If an IP responds to ping it is put\n// on the returned success channel, otherwise it is put on the fail channel.\nfunc scanPrefixes(ipCh chan net.IP) chan record {\n\tch := make(chan record, 1)\n\tgo func() {\n\t\tdefer close(ch)\n\n\t\tlimit := make(chan struct{}, 100)\n\t\twg := sync.WaitGroup{}\n\t\tfor ip := range ipCh {\n\t\t\tlimit <- struct{}{}\n\t\t\twg.Add(1)\n\t\t\tgo func(ip net.IP) {\n\t\t\t\tdefer func() { <-limit }()\n\t\t\t\tdefer wg.Done()\n\n\t\t\t\tctx, cancel := context.WithTimeout(\n\t\t\t\t\tcontext.Background(),\n\t\t\t\t\t3*time.Second,\n\t\t\t\t)\n\t\t\t\tdefer cancel()\n\n\t\t\t\trec := record{Host: ip}\n\t\t\t\tif hostAlive(ctx, ip) {\n\t\t\t\t\trec.Reachable = true\n\t\t\t\t}\n\t\t\t\tch <- rec\n\t\t\t}(ip)\n\t\t}\n\t\twg.Wait()\n\t}()\n\treturn ch\n}\n\n// unamePrefixes takes a channel of net.IP and runs \"uname -a\" on them via the ssh binary.\nfunc unamePrefixes(user string, recs chan record) chan record {\n\tch := make(chan record, 1)\n\tgo func() {\n\t\tdefer close(ch)\n\n\t\tlimit := make(chan struct{}, 100)\n\t\twg := sync.WaitGroup{}\n\t\tfor rec := range recs {\n\t\t\tif !rec.Reachable {\n\t\t\t\tch <- rec\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tlimit <- struct{}{}\n\t\t\twg.Add(1)\n\t\t\tgo func(rec record) {\n\t\t\t\tdefer func() { <-limit }()\n\t\t\t\tdefer wg.Done()\n\n\t\t\t\ttext, err := runUname(context.Background(), rec.Host, user)\n\t\t\t\tif err != nil {\n\t\t\t\t\tch <- rec\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\trec.LoginSSH = true\n\t\t\t\trec.Uname = text\n\t\t\t\tch <- rec\n\t\t\t}(rec)\n\t\t}\n\t\twg.Wait()\n\t}()\n\treturn ch\n}\n\n// hostAlive uses the \"ping\" binary on the host to test an IP. Has a 2 second timeout.\nfunc hostAlive(ctx context.Context, host net.IP) bool {\n\tcmd := exec.CommandContext(ctx, ping, \"-c\", \"1\", \"-t\", \"2\", host.String())\n\n\tif err := cmd.Run(); err != nil {\n\t\treturn false\n\t}\n\treturn true\n}\n\n// runUname will attempt to use the \"ssh\" binary to log into a host and run \"uname -a\".\n// This will return the output of that command.\nfunc runUname(ctx context.Context, host net.IP, user string) (string, error) {\n\tif _, ok := ctx.Deadline(); !ok {\n\t\tvar cancel context.CancelFunc\n\t\tctx, cancel = context.WithTimeout(ctx, 5*time.Second)\n\t\tdefer cancel()\n\t}\n\n\tlogin := fmt.Sprintf(\"%s@%s\", user, host)\n\tcmd := exec.CommandContext(\n\t\tctx,\n\t\tssh,\n\t\t\"-o StrictHostKeyChecking=no\",\n\t\t\"-o BatchMode=yes\",\n\t\tlogin,\n\t\t\"uname -a\",\n\t)\n\tout, err := cmd.CombinedOutput()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(out), nil\n}\n"
  },
  {
    "path": "chapter/8/ssh/client/expect/expect.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\t\"os/user\"\n\t\"regexp\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/google/goexpect\"\n\t\"golang.org/x/crypto/ssh\"\n\t\"golang.org/x/crypto/ssh/terminal\"\n)\n\nvar (\n\tprivate = flag.String(\"private\", \"\", \"The path to the SSH private key for this connection\")\n)\n\nfunc main() {\n\tflag.Parse()\n\n\tif len(os.Args) != 2 {\n\t\tfmt.Println(\"Error: command must be 1 arg, [host]\")\n\t\tos.Exit(1)\n\t}\n\t_, _, err := net.SplitHostPort(os.Args[1])\n\tif err != nil {\n\t\tos.Args[1] = os.Args[1] + \":22\"\n\t\t_, _, err = net.SplitHostPort(os.Args[1])\n\t\tif err != nil {\n\t\t\tfmt.Println(\"Error: problem with host passed: \", err)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\n\tvar auth ssh.AuthMethod\n\n\tif *private == \"\" {\n\t\tfi, _ := os.Stdin.Stat()\n\t\tif (fi.Mode() & os.ModeCharDevice) == 0 {\n\t\t\tfmt.Println(\"-private not set, cannot use password when STDIN is a pipe\")\n\t\t\tos.Exit(1)\n\t\t}\n\t\tauth, err = passwordFromTerm()\n\t\tif err != nil {\n\t\t\tfmt.Println(err)\n\t\t\tos.Exit(1)\n\t\t}\n\t} else {\n\t\tauth, err = publicKey(*private)\n\t\tif err != nil {\n\t\t\tfmt.Println(err)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\n\tu, err := user.Current()\n\tif err != nil {\n\t\tfmt.Println(\"Error: problem getting current user: \", err)\n\t\tos.Exit(1)\n\t}\n\n\tconfig := &ssh.ClientConfig{\n\t\tUser:            u.Username,\n\t\tAuth:            []ssh.AuthMethod{auth},\n\t\tHostKeyCallback: ssh.InsecureIgnoreHostKey(),\n\t\tTimeout:         5 * time.Second,\n\t}\n\n\tconn, err := ssh.Dial(\"tcp\", os.Args[1], config)\n\tif err != nil {\n\t\tfmt.Println(\"Error: could not dial host: \", err)\n\t\tos.Exit(1)\n\t}\n\tdefer conn.Close()\n\n\tfmt.Println(\"Installing Expect on remote system\")\n\n\tif err := installExpect(conn); err != nil {\n\t\tfmt.Println(\"Error: \", err)\n\t\tos.Exit(1)\n\t}\n\n\tfmt.Println(\"Done\")\n}\n\nfunc passwordFromTerm() (ssh.AuthMethod, error) {\n\tfmt.Printf(\"SSH Passsword: \")\n\tp, err := terminal.ReadPassword(int(os.Stdin.Fd()))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfmt.Println(\"\") // Show the return\n\tif len(bytes.TrimSpace(p)) == 0 {\n\t\treturn nil, fmt.Errorf(\"password was an empty string\")\n\t}\n\treturn ssh.Password(string(p)), nil\n}\n\nfunc publicKey(privateKeyFile string) (ssh.AuthMethod, error) {\n\tk, err := os.ReadFile(privateKeyFile)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsigner, err := ssh.ParsePrivateKey(k)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn ssh.PublicKeys(signer), nil\n}\n\nfunc installExpect(conn *ssh.Client) (err error) {\n\t// Here we are setting up an io.Writer that will write\n\t// to our debug strings.Builder{}. If we run into an\n\t// error, the output of the command will dumpt to STDERR.\n\tr, w := io.Pipe()\n\tdebug := strings.Builder{}\n\tdebugDone := make(chan struct{})\n\tgo func() {\n\t\tio.Copy(&debug, r)\n\t\tclose(debugDone)\n\t}()\n\n\tdefer func() {\n\t\t// Wait for our io.Copy() to be done.\n\t\t<-debugDone\n\n\t\t// Only log this if we had an error.\n\t\tif err != nil {\n\t\t\tlog.Printf(\"expect debug:\\n%s\", debug.String())\n\t\t}\n\t}()\n\n\te, _, err := expect.SpawnSSH(conn, 5*time.Second, expect.Tee(w))\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer e.Close()\n\n\tvar promptRE = regexp.MustCompile(`\\$ `)\n\n\t_, _, err = e.Expect(promptRE, 10*time.Second)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"did not get shell prompt\")\n\t}\n\n\tif err := e.Send(\"sudo apt-get install expect\\n\"); err != nil {\n\t\treturn fmt.Errorf(\"error on send command: %s\", err)\n\t}\n\n\t_, _, ecase, err := e.ExpectSwitchCase(\n\t\t[]expect.Caser{\n\t\t\t&expect.Case{\n\t\t\t\tR: regexp.MustCompile(`Do you want to continue\\? \\[Y/n\\] `),\n\t\t\t\tT: expect.OK(),\n\t\t\t},\n\t\t\t&expect.Case{\n\t\t\t\tR: regexp.MustCompile(`is already the newest`),\n\t\t\t\tT: expect.OK(),\n\t\t\t},\n\t\t},\n\t\t10*time.Second,\n\t)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"apt-get install did not send what we expected\")\n\t}\n\n\tswitch ecase {\n\tcase 0:\n\t\tif err := e.Send(\"Y\\n\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\t_, _, err = e.Expect(promptRE, 10*time.Second)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"did not get shell prompt\")\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "chapter/8/ssh/client/remotecmd/remotecmd.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"flag\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"os/user\"\n\t\"time\"\n\n\t\"golang.org/x/crypto/ssh\"\n\t\"golang.org/x/crypto/ssh/terminal\"\n)\n\nvar (\n\tprivate = flag.String(\"private\", \"\", \"The path to the SSH private key for this connection\")\n)\n\nfunc main() {\n\tflag.Parse()\n\n\tif len(os.Args) != 3 {\n\t\tfmt.Println(\"Error: command must be 2 args, [host] [command]\")\n\t\tos.Exit(1)\n\t}\n\t_, _, err := net.SplitHostPort(os.Args[1])\n\tif err != nil {\n\t\tos.Args[1] = os.Args[1] + \":22\"\n\t\t_, _, err = net.SplitHostPort(os.Args[1])\n\t\tif err != nil {\n\t\t\tfmt.Println(\"Error: problem with host passed: \", err)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\n\tvar auth ssh.AuthMethod\n\n\tif *private == \"\" {\n\t\tfi, _ := os.Stdin.Stat()\n\t\tif (fi.Mode() & os.ModeCharDevice) == 0 {\n\t\t\tfmt.Println(\"-private not set, cannot use password when STDIN is a pipe\")\n\t\t\tos.Exit(1)\n\t\t}\n\t\tauth, err = passwordFromTerm()\n\t\tif err != nil {\n\t\t\tfmt.Println(err)\n\t\t\tos.Exit(1)\n\t\t}\n\t} else {\n\t\tauth, err = publicKey(*private)\n\t\tif err != nil {\n\t\t\tfmt.Println(err)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\n\tu, err := user.Current()\n\tif err != nil {\n\t\tfmt.Println(\"Error: problem getting current user: \", err)\n\t\tos.Exit(1)\n\t}\n\n\tconfig := &ssh.ClientConfig{\n\t\tUser:            u.Username,\n\t\tAuth:            []ssh.AuthMethod{auth},\n\t\tHostKeyCallback: ssh.InsecureIgnoreHostKey(),\n\t\tTimeout:         5 * time.Second,\n\t}\n\n\tconn, err := ssh.Dial(\"tcp\", os.Args[1], config)\n\tif err != nil {\n\t\tfmt.Println(\"Error: could not dial host: \", err)\n\t\tos.Exit(1)\n\t}\n\tdefer conn.Close()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)\n\tdefer cancel()\n\n\tout, err := combinedOutput(ctx, conn, os.Args[2])\n\tif err != nil {\n\t\tfmt.Println(\"command error: \", err)\n\t\tos.Exit(1)\n\t}\n\tfmt.Println(out)\n}\n\nfunc passwordFromTerm() (ssh.AuthMethod, error) {\n\tfmt.Printf(\"SSH Passsword: \")\n\tp, err := terminal.ReadPassword(int(os.Stdin.Fd()))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfmt.Println(\"\") // Show the return\n\tif len(bytes.TrimSpace(p)) == 0 {\n\t\treturn nil, fmt.Errorf(\"password was an empty string\")\n\t}\n\treturn ssh.Password(string(p)), nil\n}\n\nfunc publicKey(privateKeyFile string) (ssh.AuthMethod, error) {\n\tk, err := os.ReadFile(privateKeyFile)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsigner, err := ssh.ParsePrivateKey(k)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn ssh.PublicKeys(signer), nil\n}\n\n// combinedOutput runs a command on an SSH client. The context can be cancelled, however\n// SSH does not always honor the kill signals we send, so this might not break. So closing\n// the session does nothing. So depending on what the server is doing, cancelling the context\n// may do nothing and it may still block.\nfunc combinedOutput(ctx context.Context, conn *ssh.Client, cmd string) (string, error) {\n\tsess, err := conn.NewSession()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer sess.Close()\n\n\tif v, ok := ctx.Deadline(); ok {\n\t\tt := time.NewTimer(v.Sub(time.Now()))\n\t\tdefer t.Stop()\n\n\t\tgo func() {\n\t\t\tx := <-t.C\n\t\t\tif !x.IsZero() {\n\t\t\t\tsess.Signal(ssh.SIGKILL)\n\t\t\t}\n\t\t}()\n\t}\n\n\tb, err := sess.Output(cmd)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(b), nil\n}\n"
  },
  {
    "path": "chapter/9/alerting/alertmanager.yml",
    "content": "route:\n  receiver: default\n  group_by: [ alertname ]\n  routes:\n    - match:\n        exported_job: demo-server\n      receiver: demo-server\n\nreceivers:\n  - name: default\n    pagerduty_configs:\n      - service_key: \"**Primary-Integration-Key**\"\n\n  - name: demo-server\n    pagerduty_configs:\n      - service_key: \"**Server-Team-Integration-Key**\"\n"
  },
  {
    "path": "chapter/9/alerting/client/Dockerfile",
    "content": "FROM golang:1.17\nCOPY . /usr/src/client/\nWORKDIR /usr/src/client/\nRUN go env -w GOPROXY=direct\nRUN go install ./main.go\nCMD [\"/go/bin/main\"]\n"
  },
  {
    "path": "chapter/9/alerting/client/go.mod",
    "content": "module github.com/PacktPublishing/Go-for-DevOps/chapter/9/logging/demo/client\n\ngo 1.17\n\nrequire (\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0\n\tgo.opentelemetry.io/otel v1.3.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.26.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.26.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0\n\tgo.opentelemetry.io/otel/metric v0.26.0\n\tgo.opentelemetry.io/otel/sdk v1.3.0\n\tgo.opentelemetry.io/otel/sdk/metric v0.26.0\n\tgoogle.golang.org/grpc v1.43.0\n)\n\nrequire (\n\tgithub.com/cenkalti/backoff/v4 v4.1.2 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/felixge/httpsnoop v1.0.2 // indirect\n\tgithub.com/go-logr/logr v1.2.1 // indirect\n\tgithub.com/go-logr/stdr v1.2.0 // indirect\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgithub.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 // indirect\n\tgo.opentelemetry.io/otel/internal/metric v0.26.0 // indirect\n\tgo.opentelemetry.io/otel/sdk/export/metric v0.26.0 // indirect\n\tgo.opentelemetry.io/otel/trace v1.3.0 // indirect\n\tgo.opentelemetry.io/proto/otlp v0.11.0 // indirect\n\tgolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect\n\tgolang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect\n\tgolang.org/x/text v0.3.3 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece // indirect\n\tgoogle.golang.org/protobuf v1.27.1 // indirect\n)\n"
  },
  {
    "path": "chapter/9/alerting/client/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=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=\ngithub.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=\ngithub.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\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/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\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/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.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=\ngithub.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=\ngithub.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=\ngithub.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/protobuf v1.2.0/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.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/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.5.0/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 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0 h1:hpEoMBvKLC6CqFZogJypr9IHwwSNF3ayEkNzD502QAM=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0/go.mod h1:Ihno+mNBfZlT0Qot3XyRTdZ/9U/Cg2Pfgj75DTdIfq4=\ngo.opentelemetry.io/otel v1.3.0 h1:APxLf0eiBwLl+SOXiJJCVYzA1OOJNyAoV8C5RNRyy7Y=\ngo.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 h1:R/OBkMoGgfy2fLhs2QhkCI1w4HLEQX92GCcJB6SSdNk=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.26.0 h1:dIE9swzwOnkGaJ6OF1QQQdBk2EdrJnD9Ilao2G9DeLU=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.26.0/go.mod h1:1E0NE+3ywwedkOEl3d7nFjyI/bqRECMhI3xTGh13pxY=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.26.0 h1:uBujg02iT0vOsjBF85BgcEaMGT6RaViwA9Sz/nh4bxQ=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.26.0/go.mod h1:pK3MWIu31OABQez2HFn3IRglTfIzXZtqRtgqE8fDt9U=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0 h1:giGm8w67Ja7amYNfYMdme7xSp2pIxThWopw8+QP51Yk=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0 h1:VQbUHoJqytHHSJ1OZodPH9tvZZSVzUHjPHpkO85sT6k=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY=\ngo.opentelemetry.io/otel/internal/metric v0.26.0 h1:dlrvawyd/A+X8Jp0EBT4wWEe4k5avYaXsXrBr4dbfnY=\ngo.opentelemetry.io/otel/internal/metric v0.26.0/go.mod h1:CbBP6AxKynRs3QCbhklyLUtpfzbqCLiafV9oY2Zj1Jk=\ngo.opentelemetry.io/otel/metric v0.26.0 h1:VaPYBTvA13h/FsiWfxa3yZnZEm15BhStD8JZQSA773M=\ngo.opentelemetry.io/otel/metric v0.26.0/go.mod h1:c6YL0fhRo4YVoNs6GoByzUgBp36hBL523rECoZA5UWg=\ngo.opentelemetry.io/otel/sdk v1.3.0 h1:3278edCoH89MEJ0Ky8WQXVmDQv3FX4ZJ3Pp+9fJreAI=\ngo.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=\ngo.opentelemetry.io/otel/sdk/export/metric v0.26.0 h1:eNseg5yyZqaAAY+Att3owR3Bl0Is5rCZywqO1OrGx18=\ngo.opentelemetry.io/otel/sdk/export/metric v0.26.0/go.mod h1:UpqzSnUOjFeSIVQLPp3pYIXfB/MiMFyXXzYT/bercxQ=\ngo.opentelemetry.io/otel/sdk/metric v0.26.0 h1:7IKp3gc/ObieCtshBeYYVFp3ZP7xIH1OzODi1Wao90Y=\ngo.opentelemetry.io/otel/sdk/metric v0.26.0/go.mod h1:2VIeK0kS1YvRLFg3J58ptZTXYpiWlkq2n5RQt6w7He8=\ngo.opentelemetry.io/otel/trace v1.3.0 h1:doy8Hzb1RJ+I3yFhtDmwNc7tIyw1tNMOIsyPzp1NOGY=\ngo.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.opentelemetry.io/proto/otlp v0.11.0 h1:cLDgIBTf4lLOlztkhzAEdQsJ4Lj+i5Wc9k6Nn0K1VyU=\ngo.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ=\ngo.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=\ngo.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\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-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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\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-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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\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=\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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece h1:1YM0uhfumvoDu9sx8+RyWwTI63zoCQvI23IYFRlvte0=\ngoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM=\ngoogle.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\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/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/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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\n"
  },
  {
    "path": "chapter/9/alerting/client/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"math/rand\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\"\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlpmetric\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc\"\n\t\"go.opentelemetry.io/otel/metric\"\n\t\"go.opentelemetry.io/otel/metric/global\"\n\t\"go.opentelemetry.io/otel/propagation\"\n\tcontroller \"go.opentelemetry.io/otel/sdk/metric/controller/basic\"\n\tprocessor \"go.opentelemetry.io/otel/sdk/metric/processor/basic\"\n\t\"go.opentelemetry.io/otel/sdk/metric/selector/simple\"\n\t\"go.opentelemetry.io/otel/sdk/resource\"\n\tsdktrace \"go.opentelemetry.io/otel/sdk/trace\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.4.0\"\n\t\"google.golang.org/grpc\"\n)\n\n// main sets up the trace and metrics providers and starts a loop to continuously call the server\nfunc main() {\n\tshutdown := initTraceAndMetricsProvider()\n\tdefer shutdown()\n\n\tcontinuouslySendRequests()\n}\n\n// initTraceAndMetricsProvider initializes an OTLP exporter, and configures the corresponding trace and\n// metric providers.\nfunc initTraceAndMetricsProvider() func() {\n\tctx := context.Background()\n\n\totelAgentAddr, ok := os.LookupEnv(\"OTEL_EXPORTER_OTLP_ENDPOINT\")\n\tif !ok {\n\t\totelAgentAddr = \"0.0.0.0:4317\"\n\t}\n\n\tcloseMetrics := initMetrics(ctx, otelAgentAddr)\n\tcloseTraces := initTracer(ctx, otelAgentAddr)\n\n\treturn func() {\n\t\tdoneCtx, cancel := context.WithTimeout(ctx, time.Second)\n\t\tdefer cancel()\n\t\t// pushes any last exports to the receiver\n\t\tcloseTraces(doneCtx)\n\t\tcloseMetrics(doneCtx)\n\t}\n}\n\n// initTracer initializes an OTLP trace exporter and registers the trace provider with the global context\nfunc initTracer(ctx context.Context, otelAgentAddr string) func(context.Context) {\n\ttraceClient := otlptracegrpc.NewClient(\n\t\totlptracegrpc.WithInsecure(),\n\t\totlptracegrpc.WithEndpoint(otelAgentAddr),\n\t\totlptracegrpc.WithDialOption(grpc.WithBlock()))\n\ttraceExp, err := otlptrace.New(ctx, traceClient)\n\thandleErr(err, \"Failed to create the collector trace exporter\")\n\n\tres, err := resource.New(ctx,\n\t\tresource.WithFromEnv(),\n\t\tresource.WithProcess(),\n\t\tresource.WithTelemetrySDK(),\n\t\tresource.WithHost(),\n\t\tresource.WithAttributes(\n\t\t\t// the service name used to display traces in backends\n\t\t\tsemconv.ServiceNameKey.String(\"demo-client\"),\n\t\t),\n\t)\n\thandleErr(err, \"failed to create resource\")\n\n\tbsp := sdktrace.NewBatchSpanProcessor(traceExp)\n\ttracerProvider := sdktrace.NewTracerProvider(\n\t\tsdktrace.WithSampler(sdktrace.AlwaysSample()),\n\t\tsdktrace.WithResource(res),\n\t\tsdktrace.WithSpanProcessor(bsp),\n\t)\n\n\t// set global propagator to tracecontext (the default is no-op).\n\totel.SetTextMapPropagator(propagation.TraceContext{})\n\totel.SetTracerProvider(tracerProvider)\n\n\treturn func(doneCtx context.Context) {\n\t\tif err := traceExp.Shutdown(doneCtx); err != nil {\n\t\t\totel.Handle(err)\n\t\t}\n\t}\n}\n\n// initMetrics initializes a metrics pusher and registers the metrics provider with the global context\nfunc initMetrics(ctx context.Context, otelAgentAddr string) func(context.Context) {\n\tmetricClient := otlpmetricgrpc.NewClient(\n\t\totlpmetricgrpc.WithInsecure(),\n\t\totlpmetricgrpc.WithEndpoint(otelAgentAddr))\n\tmetricExp, err := otlpmetric.New(ctx, metricClient)\n\thandleErr(err, \"Failed to create the collector metric exporter\")\n\n\tres, err := resource.New(ctx,\n\t\tresource.WithFromEnv(),\n\t\tresource.WithProcess(),\n\t\tresource.WithTelemetrySDK(),\n\t\tresource.WithAttributes(attribute.String(\"service\", \"demo-client\")),\n\t\tresource.WithHost(),\n\t\tresource.WithAttributes(\n\t\t\t// the service name used to display traces in backends\n\t\t\tsemconv.ServiceNameKey.String(\"demo-client\"),\n\t\t),\n\t)\n\tpusher := controller.New(\n\t\tprocessor.NewFactory(\n\t\t\tsimple.NewWithHistogramDistribution(),\n\t\t\tmetricExp,\n\t\t),\n\t\tcontroller.WithExporter(metricExp),\n\t\tcontroller.WithCollectPeriod(2*time.Second),\n\t\tcontroller.WithResource(res),\n\t)\n\tglobal.SetMeterProvider(pusher)\n\n\terr = pusher.Start(ctx)\n\thandleErr(err, \"Failed to start metric pusher\")\n\n\treturn func(doneCtx context.Context) {\n\t\t// pushes any last exports to the receiver\n\t\tif err := pusher.Stop(doneCtx); err != nil {\n\t\t\totel.Handle(err)\n\t\t}\n\t}\n}\n\n// handleErr provides a simple way to handle errors and messages\nfunc handleErr(err error, message string) {\n\tif err != nil {\n\t\tlog.Fatalf(\"%s: %v\", message, err)\n\t}\n}\n\n// continuouslySendRequests continuously sends requests to the server and generates random lines of text to be measured\nfunc continuouslySendRequests() {\n\tvar (\n\t\ttracer       = otel.Tracer(\"demo-client-tracer\")\n\t\tmeter        = global.Meter(\"demo-client-meter\")\n\t\tinstruments  = NewClientInstruments(meter)\n\t\tcommonLabels = []attribute.KeyValue{\n\t\t\tattribute.String(\"method\", \"repl\"),\n\t\t\tattribute.String(\"client\", \"cli\"),\n\t\t}\n\t\trng = rand.New(rand.NewSource(time.Now().UnixNano()))\n\t)\n\n\tfor {\n\t\tstartTime := time.Now()\n\t\tctx, span := tracer.Start(context.Background(), \"ExecuteRequest\")\n\t\tmakeRequest(ctx)\n\t\tspan.End()\n\t\tlatencyMs := float64(time.Since(startTime)) / 1e6\n\t\tnr := int(rng.Int31n(7))\n\t\tfor i := 0; i < nr; i++ {\n\t\t\trandLineLength := rng.Int63n(999)\n\t\t\tmeter.RecordBatch(\n\t\t\t\tctx,\n\t\t\t\tcommonLabels,\n\t\t\t\tinstruments.LineCounts.Measurement(1),\n\t\t\t\tinstruments.LineLengths.Measurement(randLineLength),\n\t\t\t)\n\t\t\tfmt.Printf(\"#%d: LineLength: %dBy\\n\", i, randLineLength)\n\t\t}\n\n\t\tmeter.RecordBatch(\n\t\t\tctx,\n\t\t\tcommonLabels,\n\t\t\tinstruments.RequestLatency.Measurement(latencyMs),\n\t\t\tinstruments.RequestCount.Measurement(1),\n\t\t)\n\n\t\tfmt.Printf(\"Latency: %.3fms\\n\", latencyMs)\n\t\ttime.Sleep(time.Duration(1) * time.Second)\n\t}\n}\n\n// makeRequest sends requests to the server using an OTEL HTTP transport which will instrument the requests with traces.\nfunc makeRequest(ctx context.Context) {\n\n\tdemoServerAddr, ok := os.LookupEnv(\"DEMO_SERVER_ENDPOINT\")\n\tif !ok {\n\t\tdemoServerAddr = \"http://0.0.0.0:7080/hello\"\n\t}\n\n\t// Trace an HTTP client by wrapping the transport\n\tclient := http.Client{\n\t\tTransport: otelhttp.NewTransport(http.DefaultTransport),\n\t}\n\n\t// Make sure we pass the context to the request to avoid broken traces.\n\treq, err := http.NewRequestWithContext(ctx, \"GET\", demoServerAddr, nil)\n\tif err != nil {\n\t\thandleErr(err, \"failed to http request\")\n\t}\n\n\t// All requests made with this client will create spans.\n\tres, err := client.Do(req)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tres.Body.Close()\n}\n\n// ClientInstruments is a collection of instruments used to measure client requests to the server\ntype ClientInstruments struct {\n\tRequestLatency metric.Float64Histogram\n\tRequestCount   metric.Int64Counter\n\tLineLengths    metric.Int64Histogram\n\tLineCounts     metric.Int64Counter\n}\n\n// NewClientInstruments takes a meter and builds a set of instruments to be used to measure client requests to the server.\nfunc NewClientInstruments(meter metric.Meter) ClientInstruments {\n\treturn ClientInstruments{\n\t\tRequestLatency: metric.Must(meter).\n\t\t\tNewFloat64Histogram(\n\t\t\t\t\"demo_client/request_latency\",\n\t\t\t\tmetric.WithDescription(\"The latency of requests processed\"),\n\t\t\t),\n\t\tRequestCount: metric.Must(meter).\n\t\t\tNewInt64Counter(\n\t\t\t\t\"demo_client/request_counts\",\n\t\t\t\tmetric.WithDescription(\"The number of requests processed\"),\n\t\t\t),\n\t\tLineLengths: metric.Must(meter).\n\t\t\tNewInt64Histogram(\n\t\t\t\t\"demo_client/line_lengths\",\n\t\t\t\tmetric.WithDescription(\"The lengths of the various lines in\"),\n\t\t\t),\n\t\tLineCounts: metric.Must(meter).\n\t\t\tNewInt64Counter(\n\t\t\t\t\"demo_client/line_counts\",\n\t\t\t\tmetric.WithDescription(\"The counts of the lines in\"),\n\t\t\t),\n\t}\n}\n"
  },
  {
    "path": "chapter/9/alerting/docker-compose.yaml",
    "content": "version: \"2\"\nservices:\n\n  # Jaeger\n  jaeger-all-in-one:\n    image: jaegertracing/all-in-one:latest\n    ports:\n      - \"16686:16686\"\n      - \"14268\"\n      - \"14250\"\n\n  # Collector\n  otel-collector:\n    image: ${OTELCOL_IMG}\n    command: [\"--config=/etc/otel-collector-config.yaml\", \"${OTELCOL_ARGS}\"]\n    volumes:\n      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml\n    ports:\n      - \"1888:1888\"   # pprof extension\n      - \"8888:8888\"   # Prometheus metrics exposed by the collector\n      - \"8889:8889\"   # Prometheus exporter metrics\n      - \"13133:13133\" # health_check extension\n      - \"4317\"        # OTLP gRPC receiver\n      - \"55670:55679\" # zpages extension\n    depends_on:\n      - jaeger-all-in-one\n\n  demo-client:\n    build:\n      dockerfile: Dockerfile\n      context: ./client\n    environment:\n      - OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317\n      - DEMO_SERVER_ENDPOINT=http://demo-server:7080/hello\n    depends_on:\n      - demo-server\n\n  demo-server:\n    build:\n      dockerfile: Dockerfile\n      context: ./server\n    environment:\n      - OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317\n    ports:\n      - \"7080\"\n    depends_on:\n      - otel-collector\n\n  prometheus:\n    container_name: prometheus\n    image: prom/prometheus:latest\n    volumes:\n      - ./prometheus.yaml:/etc/prometheus/prometheus.yml\n      - ./rules:/etc/prometheus/rules\n    ports:\n      - \"9090:9090\"\n\n  alertmanager:\n    container_name: alertmanager\n    image: prom/alertmanager:latest\n    restart: unless-stopped\n    ports:\n      - \"9093:9093\"\n    volumes:\n      - ./alertmanager.yml:/config/alertmanager.yaml\n      - alertmanager-data:/data\n    command: --config.file=/config/alertmanager.yaml --log.level=debug\n\nvolumes:\n  alertmanager-data:\n"
  },
  {
    "path": "chapter/9/alerting/otel-collector-config.yaml",
    "content": "receivers:\n  otlp:\n    protocols:\n      grpc:\n\nexporters:\n  prometheus:\n    endpoint: \"0.0.0.0:8889\"\n    const_labels:\n      label1: value1\n  logging:\n\n  jaeger:\n    endpoint: jaeger-all-in-one:14250\n    tls:\n      insecure: true\n\nprocessors:\n  batch:\n\nservice:\n  pipelines:\n    traces:\n      receivers: [otlp]\n      processors: [batch]\n      exporters: [logging, jaeger]\n    metrics:\n      receivers: [otlp]\n      processors: [batch]\n      exporters: [logging, prometheus]\n"
  },
  {
    "path": "chapter/9/alerting/prometheus.yaml",
    "content": "scrape_configs:\n  - job_name: 'otel-collector'\n    scrape_interval: 10s\n    static_configs:\n      - targets: ['otel-collector:8889']\n      - targets: ['otel-collector:8888']\nalerting:\n  alertmanagers:\n    - scheme: http\n      static_configs:\n        - targets: [ 'alertmanager:9093' ]\nrule_files:\n  - /etc/prometheus/rules/*\n"
  },
  {
    "path": "chapter/9/alerting/readme.md",
    "content": "# Metrics with OpenTelemetry and Prometheus\n\nTODO: fill in the walk through\n\n## Running this example\n- `docker-compose up -d`\n- Once started the client application will periodically send requests to the server. Metrics will be collected for the\n  requests and responses, then exported for analysis in prometheus. To view the metrics in Prometheus, open http://localhost:9090/.\n- To see the request rate for the server see: http://localhost:9090/graph?g0.expr=rate(demo_server_request_counts%5B2m%5D)&g0.tab=0&g0.stacked=0&g0.show_exemplars=0&g0.range_input=1h\n\nIf you see something like:\n```bash\ndocker-compose up -d\nTraceback (most recent call last):\n  File \"urllib3/connectionpool.py\", line 670, in urlopen\n  File \"urllib3/connectionpool.py\", line 392, in _make_request\n```\nThis indicates that you aren't running docker. Make sure you have docker installed and it is running.\n\n## Tearing down this example\n- `docker-compose down`\n\n## Influences / Credit\nThe code in this demo was heavily influenced from the example application in https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/examples/demo\nwhich carries the following license.\n```\n// Copyright The OpenTelemetry Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//       http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n```\n\nSee also: [OTEL License](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/10cfdaac1387b4df7a525c3050ce18ec8f8068be/LICENSE\n"
  },
  {
    "path": "chapter/9/alerting/rules/demo-server.yml",
    "content": "groups:\n  - name: demo-server\n    rules:\n      - alert: HighRequestLatency\n        expr: |\n          histogram_quantile(0.5, rate(http_server_duration_bucket{exported_job=\"demo-server\"}[5m])) > 200000\n        labels:\n          severity: page\n        annotations:\n          summary: High request latency\n"
  },
  {
    "path": "chapter/9/alerting/server/Dockerfile",
    "content": "FROM golang:1.17\nCOPY . /usr/src/server/\nWORKDIR /usr/src/server/\nRUN go env -w GOPROXY=direct\nRUN go install ./main.go\nCMD [\"/go/bin/main\"]\n"
  },
  {
    "path": "chapter/9/alerting/server/go.mod",
    "content": "module github.com/PacktPublishing/Go-for-DevOps/chapter/9/logging/demo/server\n\ngo 1.17\n\nrequire (\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0\n\tgo.opentelemetry.io/otel v1.3.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.26.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.26.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0\n\tgo.opentelemetry.io/otel/metric v0.26.0\n\tgo.opentelemetry.io/otel/sdk v1.3.0\n\tgo.opentelemetry.io/otel/sdk/metric v0.26.0\n\tgo.opentelemetry.io/otel/trace v1.3.0\n\tgoogle.golang.org/grpc v1.43.0\n)\n\nrequire (\n\tgithub.com/cenkalti/backoff/v4 v4.1.2 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/felixge/httpsnoop v1.0.2 // indirect\n\tgithub.com/go-logr/logr v1.2.1 // indirect\n\tgithub.com/go-logr/stdr v1.2.0 // indirect\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgithub.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 // indirect\n\tgo.opentelemetry.io/otel/internal/metric v0.26.0 // indirect\n\tgo.opentelemetry.io/otel/sdk/export/metric v0.26.0 // indirect\n\tgo.opentelemetry.io/proto/otlp v0.11.0 // indirect\n\tgolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect\n\tgolang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect\n\tgolang.org/x/text v0.3.3 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece // indirect\n\tgoogle.golang.org/protobuf v1.27.1 // indirect\n)\n"
  },
  {
    "path": "chapter/9/alerting/server/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=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=\ngithub.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=\ngithub.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\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/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\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/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.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=\ngithub.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=\ngithub.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=\ngithub.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/protobuf v1.2.0/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.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/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.5.0/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 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0 h1:hpEoMBvKLC6CqFZogJypr9IHwwSNF3ayEkNzD502QAM=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0/go.mod h1:Ihno+mNBfZlT0Qot3XyRTdZ/9U/Cg2Pfgj75DTdIfq4=\ngo.opentelemetry.io/otel v1.3.0 h1:APxLf0eiBwLl+SOXiJJCVYzA1OOJNyAoV8C5RNRyy7Y=\ngo.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 h1:R/OBkMoGgfy2fLhs2QhkCI1w4HLEQX92GCcJB6SSdNk=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.26.0 h1:dIE9swzwOnkGaJ6OF1QQQdBk2EdrJnD9Ilao2G9DeLU=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.26.0/go.mod h1:1E0NE+3ywwedkOEl3d7nFjyI/bqRECMhI3xTGh13pxY=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.26.0 h1:uBujg02iT0vOsjBF85BgcEaMGT6RaViwA9Sz/nh4bxQ=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.26.0/go.mod h1:pK3MWIu31OABQez2HFn3IRglTfIzXZtqRtgqE8fDt9U=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0 h1:giGm8w67Ja7amYNfYMdme7xSp2pIxThWopw8+QP51Yk=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0 h1:VQbUHoJqytHHSJ1OZodPH9tvZZSVzUHjPHpkO85sT6k=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY=\ngo.opentelemetry.io/otel/internal/metric v0.26.0 h1:dlrvawyd/A+X8Jp0EBT4wWEe4k5avYaXsXrBr4dbfnY=\ngo.opentelemetry.io/otel/internal/metric v0.26.0/go.mod h1:CbBP6AxKynRs3QCbhklyLUtpfzbqCLiafV9oY2Zj1Jk=\ngo.opentelemetry.io/otel/metric v0.26.0 h1:VaPYBTvA13h/FsiWfxa3yZnZEm15BhStD8JZQSA773M=\ngo.opentelemetry.io/otel/metric v0.26.0/go.mod h1:c6YL0fhRo4YVoNs6GoByzUgBp36hBL523rECoZA5UWg=\ngo.opentelemetry.io/otel/sdk v1.3.0 h1:3278edCoH89MEJ0Ky8WQXVmDQv3FX4ZJ3Pp+9fJreAI=\ngo.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=\ngo.opentelemetry.io/otel/sdk/export/metric v0.26.0 h1:eNseg5yyZqaAAY+Att3owR3Bl0Is5rCZywqO1OrGx18=\ngo.opentelemetry.io/otel/sdk/export/metric v0.26.0/go.mod h1:UpqzSnUOjFeSIVQLPp3pYIXfB/MiMFyXXzYT/bercxQ=\ngo.opentelemetry.io/otel/sdk/metric v0.26.0 h1:7IKp3gc/ObieCtshBeYYVFp3ZP7xIH1OzODi1Wao90Y=\ngo.opentelemetry.io/otel/sdk/metric v0.26.0/go.mod h1:2VIeK0kS1YvRLFg3J58ptZTXYpiWlkq2n5RQt6w7He8=\ngo.opentelemetry.io/otel/trace v1.3.0 h1:doy8Hzb1RJ+I3yFhtDmwNc7tIyw1tNMOIsyPzp1NOGY=\ngo.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.opentelemetry.io/proto/otlp v0.11.0 h1:cLDgIBTf4lLOlztkhzAEdQsJ4Lj+i5Wc9k6Nn0K1VyU=\ngo.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ=\ngo.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=\ngo.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\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-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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\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-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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\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=\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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece h1:1YM0uhfumvoDu9sx8+RyWwTI63zoCQvI23IYFRlvte0=\ngoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM=\ngoogle.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\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/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/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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\n"
  },
  {
    "path": "chapter/9/alerting/server/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"math/rand\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\"\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlpmetric\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc\"\n\t\"go.opentelemetry.io/otel/metric\"\n\t\"go.opentelemetry.io/otel/metric/global\"\n\t\"go.opentelemetry.io/otel/propagation\"\n\tcontroller \"go.opentelemetry.io/otel/sdk/metric/controller/basic\"\n\tprocessor \"go.opentelemetry.io/otel/sdk/metric/processor/basic\"\n\t\"go.opentelemetry.io/otel/sdk/metric/selector/simple\"\n\t\"go.opentelemetry.io/otel/sdk/resource\"\n\tsdktrace \"go.opentelemetry.io/otel/sdk/trace\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.4.0\"\n\t\"go.opentelemetry.io/otel/trace\"\n\t\"google.golang.org/grpc\"\n)\n\nvar rng = rand.New(rand.NewSource(time.Now().UnixNano()))\n\n// main initializes metrics and tracing providers and listens to requests at /hello returning \"Hello World!\" with\n// randomized latency.\nfunc main() {\n\tshutdown := initProvider()\n\tdefer shutdown()\n\n\t// create a handler wrapped in OpenTelemetry instrumentation\n\thandler := handleRequestWithRandomSleep()\n\twrappedHandler := otelhttp.NewHandler(handler, \"/hello\")\n\n\t// serve up the wrapped handler\n\thttp.Handle(\"/hello\", wrappedHandler)\n\thttp.ListenAndServe(\":7080\", nil)\n}\n\n// handleRequestWithRandomSleep registers a request handler that will record request counts and randomly sleep to induce\n// artificial request latency.\nfunc handleRequestWithRandomSleep() http.HandlerFunc {\n\tvar (\n\t\tmeter        = global.Meter(\"demo-server-meter\")\n\t\tinstruments  = NewServerInstruments(meter)\n\t\tcommonLabels = []attribute.KeyValue{\n\t\t\tattribute.String(\"server-attribute\", \"foo\"),\n\t\t}\n\t)\n\n\treturn func(w http.ResponseWriter, req *http.Request) {\n\t\t//  random sleep to simulate latency\n\t\tvar sleep int64\n\t\tswitch modulus := time.Now().Unix() % 5; modulus {\n\t\tcase 0:\n\t\t\tsleep = rng.Int63n(2000)\n\t\tcase 1:\n\t\t\tsleep = rng.Int63n(15)\n\t\tcase 2:\n\t\t\tsleep = rng.Int63n(917)\n\t\tcase 3:\n\t\t\tsleep = rng.Int63n(87)\n\t\tcase 4:\n\t\t\tsleep = rng.Int63n(1173)\n\t\t}\n\t\ttime.Sleep(time.Duration(sleep) * time.Millisecond)\n\t\tctx := req.Context()\n\t\tmeter.RecordBatch(\n\t\t\tctx,\n\t\t\tcommonLabels,\n\t\t\tinstruments.RequestCount.Measurement(1),\n\t\t)\n\t\tspan := trace.SpanFromContext(ctx)\n\t\tspan.SetAttributes(commonLabels...)\n\t\tw.Write([]byte(\"Hello World\"))\n\t}\n}\n\n// initTraceAndMetricsProvider initializes an OTLP exporter, and configures the corresponding trace and\n// metric providers.\nfunc initProvider() func() {\n\tctx := context.Background()\n\n\totelAgentAddr, ok := os.LookupEnv(\"OTEL_EXPORTER_OTLP_ENDPOINT\")\n\tif !ok {\n\t\totelAgentAddr = \"0.0.0.0:4317\"\n\t}\n\n\tcloseMetrics := initMetrics(ctx, otelAgentAddr)\n\tcloseTraces := initTracer(ctx, otelAgentAddr)\n\n\treturn func() {\n\t\tdoneCtx, cancel := context.WithTimeout(ctx, time.Second)\n\t\tdefer cancel()\n\t\t// pushes any last exports to the receiver\n\t\tcloseTraces(doneCtx)\n\t\tcloseMetrics(doneCtx)\n\t}\n}\n\n// initTracer initializes an OTLP trace exporter and registers the trace provider with the global context\nfunc initTracer(ctx context.Context, otelAgentAddr string) func(context.Context) {\n\ttraceClient := otlptracegrpc.NewClient(\n\t\totlptracegrpc.WithInsecure(),\n\t\totlptracegrpc.WithEndpoint(otelAgentAddr),\n\t\totlptracegrpc.WithDialOption(grpc.WithBlock()))\n\ttraceExp, err := otlptrace.New(ctx, traceClient)\n\thandleErr(err, \"Failed to create the collector trace exporter\")\n\n\tres, err := resource.New(ctx,\n\t\tresource.WithFromEnv(),\n\t\tresource.WithProcess(),\n\t\tresource.WithTelemetrySDK(),\n\t\tresource.WithHost(),\n\t\tresource.WithAttributes(\n\t\t\t// the service name used to display traces in backends\n\t\t\tsemconv.ServiceNameKey.String(\"demo-server\"),\n\t\t),\n\t)\n\thandleErr(err, \"failed to create resource\")\n\n\tbsp := sdktrace.NewBatchSpanProcessor(traceExp)\n\ttracerProvider := sdktrace.NewTracerProvider(\n\t\tsdktrace.WithSampler(sdktrace.AlwaysSample()),\n\t\tsdktrace.WithResource(res),\n\t\tsdktrace.WithSpanProcessor(bsp),\n\t)\n\n\t// set global propagator to tracecontext (the default is no-op).\n\totel.SetTextMapPropagator(propagation.TraceContext{})\n\totel.SetTracerProvider(tracerProvider)\n\n\treturn func(doneCtx context.Context) {\n\t\tif err := traceExp.Shutdown(doneCtx); err != nil {\n\t\t\totel.Handle(err)\n\t\t}\n\t}\n}\n\n// initMetrics initializes a metrics pusher and registers the metrics provider with the global context\nfunc initMetrics(ctx context.Context, otelAgentAddr string) func(context.Context) {\n\tmetricClient := otlpmetricgrpc.NewClient(\n\t\totlpmetricgrpc.WithInsecure(),\n\t\totlpmetricgrpc.WithEndpoint(otelAgentAddr))\n\tmetricExp, err := otlpmetric.New(ctx, metricClient)\n\thandleErr(err, \"Failed to create the collector metric exporter\")\n\n\tres, err := resource.New(ctx,\n\t\tresource.WithFromEnv(),\n\t\tresource.WithProcess(),\n\t\tresource.WithTelemetrySDK(),\n\t\tresource.WithHost(),\n\t\tresource.WithAttributes(\n\t\t\t// the service name used to display traces in backends\n\t\t\tsemconv.ServiceNameKey.String(\"demo-server\"),\n\t\t),\n\t)\n\tpusher := controller.New(\n\t\tprocessor.NewFactory(\n\t\t\tsimple.NewWithHistogramDistribution(),\n\t\t\tmetricExp,\n\t\t),\n\t\tcontroller.WithExporter(metricExp),\n\t\tcontroller.WithCollectPeriod(2*time.Second),\n\t\tcontroller.WithResource(res),\n\t)\n\tglobal.SetMeterProvider(pusher)\n\n\terr = pusher.Start(ctx)\n\thandleErr(err, \"Failed to start metric pusher\")\n\n\treturn func(doneCtx context.Context) {\n\t\t// pushes any last exports to the receiver\n\t\tif err := pusher.Stop(doneCtx); err != nil {\n\t\t\totel.Handle(err)\n\t\t}\n\t}\n}\n\nfunc handleErr(err error, message string) {\n\tif err != nil {\n\t\tlog.Fatalf(\"%s: %v\", message, err)\n\t}\n}\n\n// ServerInstruments contains the metric instruments used by the server\ntype ServerInstruments struct {\n\tRequestCount metric.Int64Counter\n}\n\n// NewServerInstruments takes a meter and builds a request count instrument to be used to measure server received requests.\nfunc NewServerInstruments(meter metric.Meter) ServerInstruments {\n\treturn ServerInstruments{\n\t\tRequestCount: metric.Must(meter).NewInt64Counter(\n\t\t\t\"demo_server/request_counts\",\n\t\t\tmetric.WithDescription(\"The number of requests received\"),\n\t\t),\n\t}\n}\n"
  },
  {
    "path": "chapter/9/logging/docker-compose.yml",
    "content": "version: \"3\"\nservices:\n  opentelemetry-collector-contrib:\n    image: otel/opentelemetry-collector-contrib-dev:latest\n    command: [\"--config=/etc/otel-collector-config.yml\"]\n    volumes:\n      - ./otel-collector-config.yml:/etc/otel-collector-config.yml\n      - ./varlogpods:/var/log/pods\n"
  },
  {
    "path": "chapter/9/logging/otel-collector-config.yml",
    "content": "receivers:\n  filelog:\n    include:\n      - /var/log/pods/*/*/*.log\n    exclude:\n      # Exclude logs from all containers named otel-collector\n      - /var/log/pods/*/otel-collector/*.log\n    start_at: beginning\n    include_file_path: true\n    include_file_name: false\n    operators:\n      # Find out which format is used by kubernetes\n      - type: router\n        id: get-format\n        routes:\n          - output: parser-docker\n            expr: '$$body matches \"^\\\\{\"'\n          - output: parser-crio\n            expr: '$$body matches \"^[^ Z]+ \"'\n          - output: parser-containerd\n            expr: '$$body matches \"^[^ Z]+Z\"'\n      # Parse CRI-O format\n      - type: regex_parser\n        id: parser-crio\n        regex: '^(?P<time>[^ Z]+) (?P<stream>stdout|stderr) (?P<logtag>[^ ]*) (?P<log>.*)$'\n        output: extract_metadata_from_filepath\n        timestamp:\n          parse_from: time\n          layout_type: gotime\n          layout: '2006-01-02T15:04:05.000000000-07:00'\n      # Parse CRI-Containerd format\n      - type: regex_parser\n        id: parser-containerd\n        regex: '^(?P<time>[^ ^Z]+Z) (?P<stream>stdout|stderr) (?P<logtag>[^ ]*) (?P<log>.*)$'\n        output: extract_metadata_from_filepath\n        timestamp:\n          parse_from: time\n          layout: '%Y-%m-%dT%H:%M:%S.%LZ'\n      # Parse Docker format\n      - type: json_parser\n        id: parser-docker\n        output: extract_metadata_from_filepath\n        timestamp:\n          parse_from: time\n          layout: '%Y-%m-%dT%H:%M:%S.%LZ'\n      # Extract metadata from file path\n      - type: regex_parser\n        id: extract_metadata_from_filepath\n        regex: '^.*\\/(?P<namespace>[^_]+)_(?P<pod_name>[^_]+)_(?P<uid>[a-f0-9\\-]{36})\\/(?P<container_name>[^\\._]+)\\/(?P<restart_count>\\d+)\\.log$'\n        parse_from: $$attributes[\"file.path\"]\n      # Move out attributes to Attributes\n      - type: metadata\n        attributes:\n          stream: 'EXPR($.stream)'\n          k8s.container.name: 'EXPR($.container_name)'\n          k8s.namespace.name: 'EXPR($.namespace)'\n          k8s.pod.name: 'EXPR($.pod_name)'\n          k8s.container.restart_count: 'EXPR($.restart_count)'\n          k8s.pod.uid: 'EXPR($.uid)'\n      # Clean up log body\n      - type: restructure\n        id: clean-up-log-body\n        ops:\n          - move:\n              from: log\n              to: $\nexporters:\n  logging:\n    loglevel: debug\nservice:\n  pipelines:\n    logs:\n      receivers: [filelog]\n      exporters: [logging]\n"
  },
  {
    "path": "chapter/9/logging/readme.md",
    "content": "# Log ingestion, transformation, and export with OpenTelemetry\n\nTODO: fill in the walk through\n\n## Running this example\n- `docker-compose up`\n- You should see the ingested, parsed, and normalized logs exported to STDOUT.\n\n\n## Tearing down this example\n- `docker-compose down`\n\n## Influences / Credit\nThe code in this demo was heavily influenced from the example application in https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/examples/kubernetes\nwhich carries the following license.\n```\n// Copyright The OpenTelemetry Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//       http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n```\n\nSee also: [OTEL License](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/10cfdaac1387b4df7a525c3050ce18ec8f8068be/LICENSE\n"
  },
  {
    "path": "chapter/9/logging/varlogpods/containerd_logs-0_000011112222333344445555666677778888/logs/0.log",
    "content": "2021-02-16T09:21:15.518430714Z stdout F example: 10 Tue Feb 16 09:21:15 UTC 2021\n2021-02-16T09:21:16.519721603Z stdout F example: 11 Tue Feb 16 09:21:16 UTC 2021\n2021-02-16T09:21:17.521307236Z stdout F example: 12 Tue Feb 16 09:21:17 UTC 2021\n2021-02-16T09:21:18.522445945Z stdout F example: 13 (part of log) Tue Feb 16 09:21:18 UTC 2021\n2021-02-16T09:21:18.522445945Z stdout F example: 13 (end of log) Tue Feb 16 09:21:18 UTC 2021\n2021-02-16T09:21:19.523759881Z stdout F example: 14 Tue Feb 16 09:21:19 UTC 2021\n2021-02-16T09:21:20.545525544Z stdout F example: 15 Tue Feb 16 09:21:20 UTC 2021\n"
  },
  {
    "path": "chapter/9/logging/varlogpods/crio_logs-0_111122223333444455556666777788889999/logs/0.log",
    "content": "2021-02-16T08:59:31.252009327+00:00 stdout F example: 11 Tue Feb 16 08:59:31 UTC 2021\n2021-02-16T08:59:32.253038336+00:00 stdout F example: 12 Tue Feb 16 08:59:32 UTC 2021\n2021-02-16T08:59:33.254364766+00:00 stdout P example: 13 (part of log) Tue Feb 16 08:59:33 UTC 2021\n2021-02-16T08:59:33.254364766+00:00 stdout F example: 13 (end of log) Tue Feb 16 08:59:33 UTC 2021\n2021-02-16T08:59:34.255644946+00:00 stdout F example: 14 Tue Feb 16 08:59:34 UTC 2021\n2021-02-16T08:59:35.261801788+00:00 stdout F example: 15 Tue Feb 16 08:59:35 UTC 2021\n"
  },
  {
    "path": "chapter/9/logging/varlogpods/docker_logs-0_222233334444555566667777888899990000/logs/0.log",
    "content": "{\"log\":\"example: 12 Tue Feb 16 09:15:12 UTC 2021\\n\",\"stream\":\"stdout\",\"time\":\"2021-02-16T09:15:12.50286486Z\"}\n{\"log\":\"example: 13 (part of log) Tue Feb 16 09:15:13 UTC 2021\",\"stream\":\"stdout\",\"time\":\"2021-02-16T09:15:13.503624173Z\"}\n{\"log\":\"example: 13 (end of log) Tue Feb 16 09:15:13 UTC 2021\\n\",\"stream\":\"stdout\",\"time\":\"2021-02-16T09:15:13.503624173Z\"}\n{\"log\":\"example: 14 Tue Feb 16 09:15:14 UTC 2021\\n\",\"stream\":\"stdout\",\"time\":\"2021-02-16T09:15:14.504446654Z\"}\n{\"log\":\"example: 15 Tue Feb 16 09:15:15 UTC 2021\\n\",\"stream\":\"stdout\",\"time\":\"2021-02-16T09:15:15.505407523Z\"}\n{\"log\":\"example: 16 Tue Feb 16 09:15:16 UTC 2021\\n\",\"stream\":\"stdout\",\"time\":\"2021-02-16T09:15:16.51048724Z\"}\n{\"log\":\"example: 17 Tue Feb 16 09:15:17 UTC 2021\\n\",\"stream\":\"stdout\",\"time\":\"2021-02-16T09:15:17.511829776Z\"}\n"
  },
  {
    "path": "chapter/9/logging/varlogpods/otel_otel_888877776666555544443333222211110000/otel-collector/0.log",
    "content": "2021-02-16T09:21:15.518430714Z stdout F otel-collector: 10 Tue Feb 16 09:21:15 UTC 2021\n2021-02-16T09:21:16.519721603Z stdout F otel-collector: 11 Tue Feb 16 09:21:16 UTC 2021\n2021-02-16T09:21:17.521307236Z stdout F otel-collector: 12 Tue Feb 16 09:21:17 UTC 2021\n2021-02-16T09:21:18.522445945Z stdout F otel-collector: 13 (part of log) Tue Feb 16 09:21:18 UTC 2021\n2021-02-16T09:21:18.522445945Z stdout F otel-collector: 13 (end of log) Tue Feb 16 09:21:18 UTC 2021\n2021-02-16T09:21:19.523759881Z stdout F otel-collector: 14 Tue Feb 16 09:21:19 UTC 2021\n2021-02-16T09:21:20.545525544Z stdout F otel-collector: 15 Tue Feb 16 09:21:20 UTC 2021\n"
  },
  {
    "path": "chapter/9/metrics/client/Dockerfile",
    "content": "FROM golang:1.17\nCOPY . /usr/src/client/\nWORKDIR /usr/src/client/\nRUN go env -w GOPROXY=direct\nRUN go install ./main.go\nCMD [\"/go/bin/main\"]\n"
  },
  {
    "path": "chapter/9/metrics/client/go.mod",
    "content": "module github.com/PacktPublishing/Go-for-DevOps/chapter/9/logging/demo/client\n\ngo 1.17\n\nrequire (\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0\n\tgo.opentelemetry.io/otel v1.3.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.26.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.26.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0\n\tgo.opentelemetry.io/otel/metric v0.26.0\n\tgo.opentelemetry.io/otel/sdk v1.3.0\n\tgo.opentelemetry.io/otel/sdk/metric v0.26.0\n\tgoogle.golang.org/grpc v1.43.0\n)\n\nrequire (\n\tgithub.com/cenkalti/backoff/v4 v4.1.2 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/felixge/httpsnoop v1.0.2 // indirect\n\tgithub.com/go-logr/logr v1.2.1 // indirect\n\tgithub.com/go-logr/stdr v1.2.0 // indirect\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgithub.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 // indirect\n\tgo.opentelemetry.io/otel/internal/metric v0.26.0 // indirect\n\tgo.opentelemetry.io/otel/sdk/export/metric v0.26.0 // indirect\n\tgo.opentelemetry.io/otel/trace v1.3.0 // indirect\n\tgo.opentelemetry.io/proto/otlp v0.11.0 // indirect\n\tgolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect\n\tgolang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect\n\tgolang.org/x/text v0.3.3 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece // indirect\n\tgoogle.golang.org/protobuf v1.27.1 // indirect\n)\n"
  },
  {
    "path": "chapter/9/metrics/client/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=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=\ngithub.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=\ngithub.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\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/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\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/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.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=\ngithub.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=\ngithub.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=\ngithub.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/protobuf v1.2.0/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.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/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.5.0/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 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0 h1:hpEoMBvKLC6CqFZogJypr9IHwwSNF3ayEkNzD502QAM=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0/go.mod h1:Ihno+mNBfZlT0Qot3XyRTdZ/9U/Cg2Pfgj75DTdIfq4=\ngo.opentelemetry.io/otel v1.3.0 h1:APxLf0eiBwLl+SOXiJJCVYzA1OOJNyAoV8C5RNRyy7Y=\ngo.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 h1:R/OBkMoGgfy2fLhs2QhkCI1w4HLEQX92GCcJB6SSdNk=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.26.0 h1:dIE9swzwOnkGaJ6OF1QQQdBk2EdrJnD9Ilao2G9DeLU=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.26.0/go.mod h1:1E0NE+3ywwedkOEl3d7nFjyI/bqRECMhI3xTGh13pxY=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.26.0 h1:uBujg02iT0vOsjBF85BgcEaMGT6RaViwA9Sz/nh4bxQ=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.26.0/go.mod h1:pK3MWIu31OABQez2HFn3IRglTfIzXZtqRtgqE8fDt9U=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0 h1:giGm8w67Ja7amYNfYMdme7xSp2pIxThWopw8+QP51Yk=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0 h1:VQbUHoJqytHHSJ1OZodPH9tvZZSVzUHjPHpkO85sT6k=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY=\ngo.opentelemetry.io/otel/internal/metric v0.26.0 h1:dlrvawyd/A+X8Jp0EBT4wWEe4k5avYaXsXrBr4dbfnY=\ngo.opentelemetry.io/otel/internal/metric v0.26.0/go.mod h1:CbBP6AxKynRs3QCbhklyLUtpfzbqCLiafV9oY2Zj1Jk=\ngo.opentelemetry.io/otel/metric v0.26.0 h1:VaPYBTvA13h/FsiWfxa3yZnZEm15BhStD8JZQSA773M=\ngo.opentelemetry.io/otel/metric v0.26.0/go.mod h1:c6YL0fhRo4YVoNs6GoByzUgBp36hBL523rECoZA5UWg=\ngo.opentelemetry.io/otel/sdk v1.3.0 h1:3278edCoH89MEJ0Ky8WQXVmDQv3FX4ZJ3Pp+9fJreAI=\ngo.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=\ngo.opentelemetry.io/otel/sdk/export/metric v0.26.0 h1:eNseg5yyZqaAAY+Att3owR3Bl0Is5rCZywqO1OrGx18=\ngo.opentelemetry.io/otel/sdk/export/metric v0.26.0/go.mod h1:UpqzSnUOjFeSIVQLPp3pYIXfB/MiMFyXXzYT/bercxQ=\ngo.opentelemetry.io/otel/sdk/metric v0.26.0 h1:7IKp3gc/ObieCtshBeYYVFp3ZP7xIH1OzODi1Wao90Y=\ngo.opentelemetry.io/otel/sdk/metric v0.26.0/go.mod h1:2VIeK0kS1YvRLFg3J58ptZTXYpiWlkq2n5RQt6w7He8=\ngo.opentelemetry.io/otel/trace v1.3.0 h1:doy8Hzb1RJ+I3yFhtDmwNc7tIyw1tNMOIsyPzp1NOGY=\ngo.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.opentelemetry.io/proto/otlp v0.11.0 h1:cLDgIBTf4lLOlztkhzAEdQsJ4Lj+i5Wc9k6Nn0K1VyU=\ngo.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ=\ngo.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=\ngo.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\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-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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\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-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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\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=\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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece h1:1YM0uhfumvoDu9sx8+RyWwTI63zoCQvI23IYFRlvte0=\ngoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM=\ngoogle.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\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/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/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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\n"
  },
  {
    "path": "chapter/9/metrics/client/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"math/rand\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\"\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlpmetric\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc\"\n\t\"go.opentelemetry.io/otel/metric\"\n\t\"go.opentelemetry.io/otel/metric/global\"\n\t\"go.opentelemetry.io/otel/propagation\"\n\tcontroller \"go.opentelemetry.io/otel/sdk/metric/controller/basic\"\n\tprocessor \"go.opentelemetry.io/otel/sdk/metric/processor/basic\"\n\t\"go.opentelemetry.io/otel/sdk/metric/selector/simple\"\n\t\"go.opentelemetry.io/otel/sdk/resource\"\n\tsdktrace \"go.opentelemetry.io/otel/sdk/trace\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.4.0\"\n\t\"google.golang.org/grpc\"\n)\n\n// main sets up the trace and metrics providers and starts a loop to continuously call the server\nfunc main() {\n\tshutdown := initTraceAndMetricsProvider()\n\tdefer shutdown()\n\n\tcontinuouslySendRequests()\n}\n\n// initTraceAndMetricsProvider initializes an OTLP exporter, and configures the corresponding trace and\n// metric providers.\nfunc initTraceAndMetricsProvider() func() {\n\tctx := context.Background()\n\n\totelAgentAddr, ok := os.LookupEnv(\"OTEL_EXPORTER_OTLP_ENDPOINT\")\n\tif !ok {\n\t\totelAgentAddr = \"0.0.0.0:4317\"\n\t}\n\n\tcloseMetrics := initMetrics(ctx, otelAgentAddr)\n\tcloseTraces := initTracer(ctx, otelAgentAddr)\n\n\treturn func() {\n\t\tdoneCtx, cancel := context.WithTimeout(ctx, time.Second)\n\t\tdefer cancel()\n\t\t// pushes any last exports to the receiver\n\t\tcloseTraces(doneCtx)\n\t\tcloseMetrics(doneCtx)\n\t}\n}\n\n// initTracer initializes an OTLP trace exporter and registers the trace provider with the global context\nfunc initTracer(ctx context.Context, otelAgentAddr string) func(context.Context) {\n\ttraceClient := otlptracegrpc.NewClient(\n\t\totlptracegrpc.WithInsecure(),\n\t\totlptracegrpc.WithEndpoint(otelAgentAddr),\n\t\totlptracegrpc.WithDialOption(grpc.WithBlock()))\n\ttraceExp, err := otlptrace.New(ctx, traceClient)\n\thandleErr(err, \"Failed to create the collector trace exporter\")\n\n\tres, err := resource.New(ctx,\n\t\tresource.WithFromEnv(),\n\t\tresource.WithProcess(),\n\t\tresource.WithTelemetrySDK(),\n\t\tresource.WithHost(),\n\t\tresource.WithAttributes(\n\t\t\t// the service name used to display traces in backends\n\t\t\tsemconv.ServiceNameKey.String(\"demo-client\"),\n\t\t),\n\t)\n\thandleErr(err, \"failed to create resource\")\n\n\tbsp := sdktrace.NewBatchSpanProcessor(traceExp)\n\ttracerProvider := sdktrace.NewTracerProvider(\n\t\tsdktrace.WithSampler(sdktrace.AlwaysSample()),\n\t\tsdktrace.WithResource(res),\n\t\tsdktrace.WithSpanProcessor(bsp),\n\t)\n\n\t// set global propagator to tracecontext (the default is no-op).\n\totel.SetTextMapPropagator(propagation.TraceContext{})\n\totel.SetTracerProvider(tracerProvider)\n\n\treturn func(doneCtx context.Context) {\n\t\tif err := traceExp.Shutdown(doneCtx); err != nil {\n\t\t\totel.Handle(err)\n\t\t}\n\t}\n}\n\n// initMetrics initializes a metrics pusher and registers the metrics provider with the global context\nfunc initMetrics(ctx context.Context, otelAgentAddr string) func(context.Context) {\n\tmetricClient := otlpmetricgrpc.NewClient(\n\t\totlpmetricgrpc.WithInsecure(),\n\t\totlpmetricgrpc.WithEndpoint(otelAgentAddr))\n\tmetricExp, err := otlpmetric.New(ctx, metricClient)\n\thandleErr(err, \"Failed to create the collector metric exporter\")\n\n\tpusher := controller.New(\n\t\tprocessor.NewFactory(\n\t\t\tsimple.NewWithHistogramDistribution(),\n\t\t\tmetricExp,\n\t\t),\n\t\tcontroller.WithExporter(metricExp),\n\t\tcontroller.WithCollectPeriod(2*time.Second),\n\t)\n\tglobal.SetMeterProvider(pusher)\n\n\terr = pusher.Start(ctx)\n\thandleErr(err, \"Failed to start metric pusher\")\n\n\treturn func(doneCtx context.Context) {\n\t\t// pushes any last exports to the receiver\n\t\tif err := pusher.Stop(doneCtx); err != nil {\n\t\t\totel.Handle(err)\n\t\t}\n\t}\n}\n\n// handleErr provides a simple way to handle errors and messages\nfunc handleErr(err error, message string) {\n\tif err != nil {\n\t\tlog.Fatalf(\"%s: %v\", message, err)\n\t}\n}\n\n// continuouslySendRequests continuously sends requests to the server and generates random lines of text to be measured\nfunc continuouslySendRequests() {\n\tvar (\n\t\ttracer       = otel.Tracer(\"demo-client-tracer\")\n\t\tmeter        = global.Meter(\"demo-client-meter\")\n\t\tinstruments  = NewClientInstruments(meter)\n\t\tcommonLabels = []attribute.KeyValue{\n\t\t\tattribute.String(\"method\", \"repl\"),\n\t\t\tattribute.String(\"client\", \"cli\"),\n\t\t}\n\t\trng = rand.New(rand.NewSource(time.Now().UnixNano()))\n\t)\n\n\tfor {\n\t\tstartTime := time.Now()\n\t\tctx, span := tracer.Start(context.Background(), \"ExecuteRequest\")\n\t\tmakeRequest(ctx)\n\t\tspan.End()\n\t\tlatencyMs := float64(time.Since(startTime)) / 1e6\n\t\tnr := int(rng.Int31n(7))\n\t\tfor i := 0; i < nr; i++ {\n\t\t\trandLineLength := rng.Int63n(999)\n\t\t\tmeter.RecordBatch(\n\t\t\t\tctx,\n\t\t\t\tcommonLabels,\n\t\t\t\tinstruments.LineCounts.Measurement(1),\n\t\t\t\tinstruments.LineLengths.Measurement(randLineLength),\n\t\t\t)\n\t\t\tfmt.Printf(\"#%d: LineLength: %dBy\\n\", i, randLineLength)\n\t\t}\n\n\t\tmeter.RecordBatch(\n\t\t\tctx,\n\t\t\tcommonLabels,\n\t\t\tinstruments.RequestLatency.Measurement(latencyMs),\n\t\t\tinstruments.RequestCount.Measurement(1),\n\t\t)\n\n\t\tfmt.Printf(\"Latency: %.3fms\\n\", latencyMs)\n\t\ttime.Sleep(time.Duration(1) * time.Second)\n\t}\n}\n\n// makeRequest sends requests to the server using an OTEL HTTP transport which will instrument the requests with traces.\nfunc makeRequest(ctx context.Context) {\n\n\tdemoServerAddr, ok := os.LookupEnv(\"DEMO_SERVER_ENDPOINT\")\n\tif !ok {\n\t\tdemoServerAddr = \"http://0.0.0.0:7080/hello\"\n\t}\n\n\t// Trace an HTTP client by wrapping the transport\n\tclient := http.Client{\n\t\tTransport: otelhttp.NewTransport(http.DefaultTransport),\n\t}\n\n\t// Make sure we pass the context to the request to avoid broken traces.\n\treq, err := http.NewRequestWithContext(ctx, \"GET\", demoServerAddr, nil)\n\tif err != nil {\n\t\thandleErr(err, \"failed to http request\")\n\t}\n\n\t// All requests made with this client will create spans.\n\tres, err := client.Do(req)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tres.Body.Close()\n}\n\n// ClientInstruments is a collection of instruments used to measure client requests to the server\ntype ClientInstruments struct {\n\tRequestLatency metric.Float64Histogram\n\tRequestCount   metric.Int64Counter\n\tLineLengths    metric.Int64Histogram\n\tLineCounts     metric.Int64Counter\n}\n\n// NewClientInstruments takes a meter and builds a set of instruments to be used to measure client requests to the server.\nfunc NewClientInstruments(meter metric.Meter) ClientInstruments {\n\treturn ClientInstruments{\n\t\tRequestLatency: metric.Must(meter).\n\t\t\tNewFloat64Histogram(\n\t\t\t\t\"demo_client/request_latency\",\n\t\t\t\tmetric.WithDescription(\"The latency of requests processed\"),\n\t\t\t),\n\t\tRequestCount: metric.Must(meter).\n\t\t\tNewInt64Counter(\n\t\t\t\t\"demo_client/request_counts\",\n\t\t\t\tmetric.WithDescription(\"The number of requests processed\"),\n\t\t\t),\n\t\tLineLengths: metric.Must(meter).\n\t\t\tNewInt64Histogram(\n\t\t\t\t\"demo_client/line_lengths\",\n\t\t\t\tmetric.WithDescription(\"The lengths of the various lines in\"),\n\t\t\t),\n\t\tLineCounts: metric.Must(meter).\n\t\t\tNewInt64Counter(\n\t\t\t\t\"demo_client/line_counts\",\n\t\t\t\tmetric.WithDescription(\"The counts of the lines in\"),\n\t\t\t),\n\t}\n}\n"
  },
  {
    "path": "chapter/9/metrics/docker-compose.yaml",
    "content": "version: \"2\"\nservices:\n\n  # Jaeger\n  jaeger-all-in-one:\n    image: jaegertracing/all-in-one:latest\n    ports:\n      - \"16686:16686\"\n      - \"14268\"\n      - \"14250\"\n\n  # Collector\n  otel-collector:\n    image: ${OTELCOL_IMG}\n    command: [\"--config=/etc/otel-collector-config.yaml\", \"${OTELCOL_ARGS}\"]\n    volumes:\n      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml\n    ports:\n      - \"1888:1888\"   # pprof extension\n      - \"8888:8888\"   # Prometheus metrics exposed by the collector\n      - \"8889:8889\"   # Prometheus exporter metrics\n      - \"13133:13133\" # health_check extension\n      - \"4317\"        # OTLP gRPC receiver\n      - \"55670:55679\" # zpages extension\n    depends_on:\n      - jaeger-all-in-one\n\n  demo-client:\n    build:\n      dockerfile: Dockerfile\n      context: ./client\n    environment:\n      - OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317\n      - DEMO_SERVER_ENDPOINT=http://demo-server:7080/hello\n    depends_on:\n      - demo-server\n\n  demo-server:\n    build:\n      dockerfile: Dockerfile\n      context: ./server\n    environment:\n      - OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317\n    ports:\n      - \"7080\"\n    depends_on:\n      - otel-collector\n\n  prometheus:\n    container_name: prometheus\n    image: prom/prometheus:latest\n    volumes:\n      - ./prometheus.yaml:/etc/prometheus/prometheus.yml\n    ports:\n      - \"9090:9090\"\n"
  },
  {
    "path": "chapter/9/metrics/otel-collector-config.yaml",
    "content": "receivers:\n  otlp:\n    protocols:\n      grpc:\n\nexporters:\n  prometheus:\n    endpoint: \"0.0.0.0:8889\"\n    const_labels:\n      label1: value1\n  logging:\n\n  jaeger:\n    endpoint: jaeger-all-in-one:14250\n    tls:\n      insecure: true\n\nprocessors:\n  batch:\n\nservice:\n  pipelines:\n    traces:\n      receivers: [otlp]\n      processors: [batch]\n      exporters: [logging, jaeger]\n    metrics:\n      receivers: [otlp]\n      processors: [batch]\n      exporters: [logging, prometheus]\n"
  },
  {
    "path": "chapter/9/metrics/prometheus.yaml",
    "content": "scrape_configs:\n  - job_name: 'otel-collector'\n    scrape_interval: 10s\n    static_configs:\n      - targets: ['otel-collector:8889']\n      - targets: ['otel-collector:8888']\n"
  },
  {
    "path": "chapter/9/metrics/readme.md",
    "content": "# Metrics with OpenTelemetry and Prometheus\n\nTODO: fill in the walk through\n\n## Running this example\n- `docker-compose up -d`\n- Once started the client application will periodically send requests to the server. Metrics will be collected for the\n  requests and responses, then exported for analysis in prometheus. To view the metrics in Prometheus, open http://localhost:9090/.\n- To see the request rate for the server see: http://localhost:9090/graph?g0.expr=rate(demo_server_request_counts%5B2m%5D)&g0.tab=0&g0.stacked=0&g0.show_exemplars=0&g0.range_input=1h\n\nIf you see something like:\n```bash\ndocker-compose up -d\nTraceback (most recent call last):\n  File \"urllib3/connectionpool.py\", line 670, in urlopen\n  File \"urllib3/connectionpool.py\", line 392, in _make_request\n```\nThis indicates that you aren't running docker. Make sure you have docker installed and it is running.\n\n## Tearing down this example\n- `docker-compose down`\n\n## Influences / Credit\nThe code in this demo was heavily influenced from the example application in https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/examples/demo\nwhich carries the following license.\n```\n// Copyright The OpenTelemetry Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//       http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n```\n\nSee also: [OTEL License](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/10cfdaac1387b4df7a525c3050ce18ec8f8068be/LICENSE\n"
  },
  {
    "path": "chapter/9/metrics/server/Dockerfile",
    "content": "FROM golang:1.17\nCOPY . /usr/src/server/\nWORKDIR /usr/src/server/\nRUN go env -w GOPROXY=direct\nRUN go install ./main.go\nCMD [\"/go/bin/main\"]\n"
  },
  {
    "path": "chapter/9/metrics/server/go.mod",
    "content": "module github.com/PacktPublishing/Go-for-DevOps/chapter/9/logging/demo/server\n\ngo 1.17\n\nrequire (\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0\n\tgo.opentelemetry.io/otel v1.3.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.26.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.26.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0\n\tgo.opentelemetry.io/otel/metric v0.26.0\n\tgo.opentelemetry.io/otel/sdk v1.3.0\n\tgo.opentelemetry.io/otel/sdk/metric v0.26.0\n\tgo.opentelemetry.io/otel/trace v1.3.0\n\tgoogle.golang.org/grpc v1.43.0\n)\n\nrequire (\n\tgithub.com/cenkalti/backoff/v4 v4.1.2 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/felixge/httpsnoop v1.0.2 // indirect\n\tgithub.com/go-logr/logr v1.2.1 // indirect\n\tgithub.com/go-logr/stdr v1.2.0 // indirect\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgithub.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 // indirect\n\tgo.opentelemetry.io/otel/internal/metric v0.26.0 // indirect\n\tgo.opentelemetry.io/otel/sdk/export/metric v0.26.0 // indirect\n\tgo.opentelemetry.io/proto/otlp v0.11.0 // indirect\n\tgolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect\n\tgolang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect\n\tgolang.org/x/text v0.3.3 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece // indirect\n\tgoogle.golang.org/protobuf v1.27.1 // indirect\n)\n"
  },
  {
    "path": "chapter/9/metrics/server/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=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=\ngithub.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=\ngithub.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\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/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\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/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.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=\ngithub.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=\ngithub.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=\ngithub.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/protobuf v1.2.0/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.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/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.5.0/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 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0 h1:hpEoMBvKLC6CqFZogJypr9IHwwSNF3ayEkNzD502QAM=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0/go.mod h1:Ihno+mNBfZlT0Qot3XyRTdZ/9U/Cg2Pfgj75DTdIfq4=\ngo.opentelemetry.io/otel v1.3.0 h1:APxLf0eiBwLl+SOXiJJCVYzA1OOJNyAoV8C5RNRyy7Y=\ngo.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 h1:R/OBkMoGgfy2fLhs2QhkCI1w4HLEQX92GCcJB6SSdNk=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.26.0 h1:dIE9swzwOnkGaJ6OF1QQQdBk2EdrJnD9Ilao2G9DeLU=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.26.0/go.mod h1:1E0NE+3ywwedkOEl3d7nFjyI/bqRECMhI3xTGh13pxY=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.26.0 h1:uBujg02iT0vOsjBF85BgcEaMGT6RaViwA9Sz/nh4bxQ=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.26.0/go.mod h1:pK3MWIu31OABQez2HFn3IRglTfIzXZtqRtgqE8fDt9U=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0 h1:giGm8w67Ja7amYNfYMdme7xSp2pIxThWopw8+QP51Yk=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0 h1:VQbUHoJqytHHSJ1OZodPH9tvZZSVzUHjPHpkO85sT6k=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY=\ngo.opentelemetry.io/otel/internal/metric v0.26.0 h1:dlrvawyd/A+X8Jp0EBT4wWEe4k5avYaXsXrBr4dbfnY=\ngo.opentelemetry.io/otel/internal/metric v0.26.0/go.mod h1:CbBP6AxKynRs3QCbhklyLUtpfzbqCLiafV9oY2Zj1Jk=\ngo.opentelemetry.io/otel/metric v0.26.0 h1:VaPYBTvA13h/FsiWfxa3yZnZEm15BhStD8JZQSA773M=\ngo.opentelemetry.io/otel/metric v0.26.0/go.mod h1:c6YL0fhRo4YVoNs6GoByzUgBp36hBL523rECoZA5UWg=\ngo.opentelemetry.io/otel/sdk v1.3.0 h1:3278edCoH89MEJ0Ky8WQXVmDQv3FX4ZJ3Pp+9fJreAI=\ngo.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=\ngo.opentelemetry.io/otel/sdk/export/metric v0.26.0 h1:eNseg5yyZqaAAY+Att3owR3Bl0Is5rCZywqO1OrGx18=\ngo.opentelemetry.io/otel/sdk/export/metric v0.26.0/go.mod h1:UpqzSnUOjFeSIVQLPp3pYIXfB/MiMFyXXzYT/bercxQ=\ngo.opentelemetry.io/otel/sdk/metric v0.26.0 h1:7IKp3gc/ObieCtshBeYYVFp3ZP7xIH1OzODi1Wao90Y=\ngo.opentelemetry.io/otel/sdk/metric v0.26.0/go.mod h1:2VIeK0kS1YvRLFg3J58ptZTXYpiWlkq2n5RQt6w7He8=\ngo.opentelemetry.io/otel/trace v1.3.0 h1:doy8Hzb1RJ+I3yFhtDmwNc7tIyw1tNMOIsyPzp1NOGY=\ngo.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.opentelemetry.io/proto/otlp v0.11.0 h1:cLDgIBTf4lLOlztkhzAEdQsJ4Lj+i5Wc9k6Nn0K1VyU=\ngo.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ=\ngo.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=\ngo.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\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-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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\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-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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\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=\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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece h1:1YM0uhfumvoDu9sx8+RyWwTI63zoCQvI23IYFRlvte0=\ngoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM=\ngoogle.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\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/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/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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\n"
  },
  {
    "path": "chapter/9/metrics/server/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"math/rand\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\"\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlpmetric\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc\"\n\t\"go.opentelemetry.io/otel/metric\"\n\t\"go.opentelemetry.io/otel/metric/global\"\n\t\"go.opentelemetry.io/otel/propagation\"\n\tcontroller \"go.opentelemetry.io/otel/sdk/metric/controller/basic\"\n\tprocessor \"go.opentelemetry.io/otel/sdk/metric/processor/basic\"\n\t\"go.opentelemetry.io/otel/sdk/metric/selector/simple\"\n\t\"go.opentelemetry.io/otel/sdk/resource\"\n\tsdktrace \"go.opentelemetry.io/otel/sdk/trace\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.4.0\"\n\t\"go.opentelemetry.io/otel/trace\"\n\t\"google.golang.org/grpc\"\n)\n\nvar rng = rand.New(rand.NewSource(time.Now().UnixNano()))\n\n// main initializes metrics and tracing providers and listens to requests at /hello returning \"Hello World!\" with\n// randomized latency.\nfunc main() {\n\tshutdown := initProvider()\n\tdefer shutdown()\n\n\t// create a handler wrapped in OpenTelemetry instrumentation\n\thandler := handleRequestWithRandomSleep()\n\twrappedHandler := otelhttp.NewHandler(handler, \"/hello\")\n\n\t// serve up the wrapped handler\n\thttp.Handle(\"/hello\", wrappedHandler)\n\thttp.ListenAndServe(\":7080\", nil)\n}\n\n// handleRequestWithRandomSleep registers a request handler that will record request counts and randomly sleep to induce\n// artificial request latency.\nfunc handleRequestWithRandomSleep() http.HandlerFunc {\n\tvar (\n\t\tmeter        = global.Meter(\"demo-server-meter\")\n\t\tinstruments  = NewServerInstruments(meter)\n\t\tcommonLabels = []attribute.KeyValue{\n\t\t\tattribute.String(\"server-attribute\", \"foo\"),\n\t\t}\n\t)\n\n\treturn func(w http.ResponseWriter, req *http.Request) {\n\t\t//  random sleep to simulate latency\n\t\tvar sleep int64\n\t\tswitch modulus := time.Now().Unix() % 5; modulus {\n\t\tcase 0:\n\t\t\tsleep = rng.Int63n(2000)\n\t\tcase 1:\n\t\t\tsleep = rng.Int63n(15)\n\t\tcase 2:\n\t\t\tsleep = rng.Int63n(917)\n\t\tcase 3:\n\t\t\tsleep = rng.Int63n(87)\n\t\tcase 4:\n\t\t\tsleep = rng.Int63n(1173)\n\t\t}\n\t\ttime.Sleep(time.Duration(sleep) * time.Millisecond)\n\t\tctx := req.Context()\n\t\tmeter.RecordBatch(\n\t\t\tctx,\n\t\t\tcommonLabels,\n\t\t\tinstruments.RequestCount.Measurement(1),\n\t\t)\n\t\tspan := trace.SpanFromContext(ctx)\n\t\tspan.SetAttributes(commonLabels...)\n\t\tw.Write([]byte(\"Hello World\"))\n\t}\n}\n\n// initTraceAndMetricsProvider initializes an OTLP exporter, and configures the corresponding trace and\n// metric providers.\nfunc initProvider() func() {\n\tctx := context.Background()\n\n\totelAgentAddr, ok := os.LookupEnv(\"OTEL_EXPORTER_OTLP_ENDPOINT\")\n\tif !ok {\n\t\totelAgentAddr = \"0.0.0.0:4317\"\n\t}\n\n\tcloseMetrics := initMetrics(ctx, otelAgentAddr)\n\tcloseTraces := initTracer(ctx, otelAgentAddr)\n\n\treturn func() {\n\t\tdoneCtx, cancel := context.WithTimeout(ctx, time.Second)\n\t\tdefer cancel()\n\t\t// pushes any last exports to the receiver\n\t\tcloseTraces(doneCtx)\n\t\tcloseMetrics(doneCtx)\n\t}\n}\n\n// initTracer initializes an OTLP trace exporter and registers the trace provider with the global context\nfunc initTracer(ctx context.Context, otelAgentAddr string) func(context.Context) {\n\ttraceClient := otlptracegrpc.NewClient(\n\t\totlptracegrpc.WithInsecure(),\n\t\totlptracegrpc.WithEndpoint(otelAgentAddr),\n\t\totlptracegrpc.WithDialOption(grpc.WithBlock()))\n\ttraceExp, err := otlptrace.New(ctx, traceClient)\n\thandleErr(err, \"Failed to create the collector trace exporter\")\n\n\tres, err := resource.New(ctx,\n\t\tresource.WithFromEnv(),\n\t\tresource.WithProcess(),\n\t\tresource.WithTelemetrySDK(),\n\t\tresource.WithHost(),\n\t\tresource.WithAttributes(\n\t\t\t// the service name used to display traces in backends\n\t\t\tsemconv.ServiceNameKey.String(\"demo-server\"),\n\t\t),\n\t)\n\thandleErr(err, \"failed to create resource\")\n\n\tbsp := sdktrace.NewBatchSpanProcessor(traceExp)\n\ttracerProvider := sdktrace.NewTracerProvider(\n\t\tsdktrace.WithSampler(sdktrace.AlwaysSample()),\n\t\tsdktrace.WithResource(res),\n\t\tsdktrace.WithSpanProcessor(bsp),\n\t)\n\n\t// set global propagator to tracecontext (the default is no-op).\n\totel.SetTextMapPropagator(propagation.TraceContext{})\n\totel.SetTracerProvider(tracerProvider)\n\n\treturn func(doneCtx context.Context) {\n\t\tif err := traceExp.Shutdown(doneCtx); err != nil {\n\t\t\totel.Handle(err)\n\t\t}\n\t}\n}\n\n// initMetrics initializes a metrics pusher and registers the metrics provider with the global context\nfunc initMetrics(ctx context.Context, otelAgentAddr string) func(context.Context) {\n\tmetricClient := otlpmetricgrpc.NewClient(\n\t\totlpmetricgrpc.WithInsecure(),\n\t\totlpmetricgrpc.WithEndpoint(otelAgentAddr))\n\tmetricExp, err := otlpmetric.New(ctx, metricClient)\n\thandleErr(err, \"Failed to create the collector metric exporter\")\n\n\tpusher := controller.New(\n\t\tprocessor.NewFactory(\n\t\t\tsimple.NewWithHistogramDistribution(),\n\t\t\tmetricExp,\n\t\t),\n\t\tcontroller.WithExporter(metricExp),\n\t\tcontroller.WithCollectPeriod(2*time.Second),\n\t)\n\tglobal.SetMeterProvider(pusher)\n\n\terr = pusher.Start(ctx)\n\thandleErr(err, \"Failed to start metric pusher\")\n\n\treturn func(doneCtx context.Context) {\n\t\t// pushes any last exports to the receiver\n\t\tif err := pusher.Stop(doneCtx); err != nil {\n\t\t\totel.Handle(err)\n\t\t}\n\t}\n}\n\nfunc handleErr(err error, message string) {\n\tif err != nil {\n\t\tlog.Fatalf(\"%s: %v\", message, err)\n\t}\n}\n\n// ServerInstruments contains the metric instruments used by the server\ntype ServerInstruments struct {\n\tRequestCount metric.Int64Counter\n}\n\n// NewServerInstruments takes a meter and builds a request count instrument to be used to measure server received requests.\nfunc NewServerInstruments(meter metric.Meter) ServerInstruments {\n\treturn ServerInstruments{\n\t\tRequestCount: metric.Must(meter).NewInt64Counter(\n\t\t\t\"demo_server/request_counts\",\n\t\t\tmetric.WithDescription(\"The number of requests received\"),\n\t\t),\n\t}\n}\n"
  },
  {
    "path": "chapter/9/tracing/client/Dockerfile",
    "content": "FROM golang:1.17\nCOPY . /usr/src/client/\nWORKDIR /usr/src/client/\nRUN go env -w GOPROXY=direct\nRUN go install ./main.go\nCMD [\"/go/bin/main\"]\n"
  },
  {
    "path": "chapter/9/tracing/client/go.mod",
    "content": "module github.com/PacktPublishing/Go-for-DevOps/chapter/9/tracing/demo/client\n\ngo 1.17\n\nrequire (\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0\n\tgo.opentelemetry.io/otel v1.3.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0\n\tgo.opentelemetry.io/otel/sdk v1.3.0\n\tgo.opentelemetry.io/otel/trace v1.3.0\n\tgo.uber.org/zap v1.19.1\n\tgoogle.golang.org/grpc v1.43.0\n)\n\nrequire (\n\tgithub.com/benbjohnson/clock v1.3.0 // indirect\n\tgithub.com/cenkalti/backoff/v4 v4.1.2 // indirect\n\tgithub.com/felixge/httpsnoop v1.0.2 // indirect\n\tgithub.com/go-logr/logr v1.2.1 // indirect\n\tgithub.com/go-logr/stdr v1.2.0 // indirect\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgithub.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 // indirect\n\tgo.opentelemetry.io/otel/internal/metric v0.26.0 // indirect\n\tgo.opentelemetry.io/otel/metric v0.26.0 // indirect\n\tgo.opentelemetry.io/proto/otlp v0.11.0 // indirect\n\tgo.uber.org/atomic v1.7.0 // indirect\n\tgo.uber.org/multierr v1.6.0 // indirect\n\tgolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect\n\tgolang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect\n\tgolang.org/x/text v0.3.3 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece // indirect\n\tgoogle.golang.org/protobuf v1.27.1 // indirect\n)\n"
  },
  {
    "path": "chapter/9/tracing/client/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=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=\ngithub.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=\ngithub.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\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/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\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/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.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=\ngithub.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=\ngithub.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=\ngithub.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/protobuf v1.2.0/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.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/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.5.0/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 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\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/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0 h1:hpEoMBvKLC6CqFZogJypr9IHwwSNF3ayEkNzD502QAM=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0/go.mod h1:Ihno+mNBfZlT0Qot3XyRTdZ/9U/Cg2Pfgj75DTdIfq4=\ngo.opentelemetry.io/otel v1.3.0 h1:APxLf0eiBwLl+SOXiJJCVYzA1OOJNyAoV8C5RNRyy7Y=\ngo.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 h1:R/OBkMoGgfy2fLhs2QhkCI1w4HLEQX92GCcJB6SSdNk=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0 h1:giGm8w67Ja7amYNfYMdme7xSp2pIxThWopw8+QP51Yk=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0 h1:VQbUHoJqytHHSJ1OZodPH9tvZZSVzUHjPHpkO85sT6k=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY=\ngo.opentelemetry.io/otel/internal/metric v0.26.0 h1:dlrvawyd/A+X8Jp0EBT4wWEe4k5avYaXsXrBr4dbfnY=\ngo.opentelemetry.io/otel/internal/metric v0.26.0/go.mod h1:CbBP6AxKynRs3QCbhklyLUtpfzbqCLiafV9oY2Zj1Jk=\ngo.opentelemetry.io/otel/metric v0.26.0 h1:VaPYBTvA13h/FsiWfxa3yZnZEm15BhStD8JZQSA773M=\ngo.opentelemetry.io/otel/metric v0.26.0/go.mod h1:c6YL0fhRo4YVoNs6GoByzUgBp36hBL523rECoZA5UWg=\ngo.opentelemetry.io/otel/sdk v1.3.0 h1:3278edCoH89MEJ0Ky8WQXVmDQv3FX4ZJ3Pp+9fJreAI=\ngo.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=\ngo.opentelemetry.io/otel/trace v1.3.0 h1:doy8Hzb1RJ+I3yFhtDmwNc7tIyw1tNMOIsyPzp1NOGY=\ngo.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.opentelemetry.io/proto/otlp v0.11.0 h1:cLDgIBTf4lLOlztkhzAEdQsJ4Lj+i5Wc9k6Nn0K1VyU=\ngo.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ=\ngo.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=\ngo.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\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-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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\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-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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\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=\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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece h1:1YM0uhfumvoDu9sx8+RyWwTI63zoCQvI23IYFRlvte0=\ngoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM=\ngoogle.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\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/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/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.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\n"
  },
  {
    "path": "chapter/9/tracing/client/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\"\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc\"\n\t\"go.opentelemetry.io/otel/propagation\"\n\t\"go.opentelemetry.io/otel/sdk/resource\"\n\tsdktrace \"go.opentelemetry.io/otel/sdk/trace\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.4.0\"\n\t\"go.opentelemetry.io/otel/trace\"\n\t\"go.uber.org/zap\"\n\t\"google.golang.org/grpc\"\n)\n\n// main sets up the trace providers and starts a loop to continuously call the server\nfunc main() {\n\tshutdown := initTraceProvider()\n\tdefer shutdown()\n\n\tcontinuouslySendRequests()\n}\n\n// initTraceProvider initializes an OTLP exporter, and configures the corresponding trace provider.\nfunc initTraceProvider() func() {\n\tctx := context.Background()\n\n\totelAgentAddr, ok := os.LookupEnv(\"OTEL_EXPORTER_OTLP_ENDPOINT\")\n\tif !ok {\n\t\totelAgentAddr = \"0.0.0.0:4317\"\n\t}\n\n\tcloseTraces := initTracer(ctx, otelAgentAddr)\n\n\treturn func() {\n\t\tdoneCtx, cancel := context.WithTimeout(ctx, time.Second)\n\t\tdefer cancel()\n\t\t// pushes any last exports to the receiver\n\t\tcloseTraces(doneCtx)\n\t}\n}\n\n// initTracer initializes an OTLP trace exporter and registers the trace provider with the global context\nfunc initTracer(ctx context.Context, otelAgentAddr string) func(context.Context) {\n\ttraceClient := otlptracegrpc.NewClient(\n\t\totlptracegrpc.WithInsecure(),\n\t\totlptracegrpc.WithEndpoint(otelAgentAddr),\n\t\totlptracegrpc.WithDialOption(grpc.WithBlock()))\n\ttraceExp, err := otlptrace.New(ctx, traceClient)\n\thandleErr(err, \"Failed to create the collector trace exporter\")\n\n\tres, err := resource.New(ctx,\n\t\tresource.WithFromEnv(),\n\t\tresource.WithProcess(),\n\t\tresource.WithTelemetrySDK(),\n\t\tresource.WithHost(),\n\t\tresource.WithAttributes(\n\t\t\t// the service name used to display traces in backends\n\t\t\tsemconv.ServiceNameKey.String(\"demo-client\"),\n\t\t),\n\t)\n\thandleErr(err, \"failed to create resource\")\n\n\tbsp := sdktrace.NewBatchSpanProcessor(traceExp)\n\ttracerProvider := sdktrace.NewTracerProvider(\n\t\tsdktrace.WithSampler(sdktrace.AlwaysSample()),\n\t\tsdktrace.WithResource(res),\n\t\tsdktrace.WithSpanProcessor(bsp),\n\t)\n\n\t// set global propagator to tracecontext (the default is no-op).\n\totel.SetTextMapPropagator(propagation.TraceContext{})\n\totel.SetTracerProvider(tracerProvider)\n\n\treturn func(doneCtx context.Context) {\n\t\tif err := traceExp.Shutdown(doneCtx); err != nil {\n\t\t\totel.Handle(err)\n\t\t}\n\t}\n}\n\n// handleErr provides a simple way to handle errors and messages\nfunc handleErr(err error, message string) {\n\tif err != nil {\n\t\tlog.Fatalf(\"%s: %v\", message, err)\n\t}\n}\n\n// continuouslySendRequests continuously sends requests to the server sleeping for a second after each request.\nfunc continuouslySendRequests() {\n\ttracer := otel.Tracer(\"demo-client-tracer\")\n\n\tfor {\n\t\tctx, span := tracer.Start(context.Background(), \"ExecuteRequest\")\n\t\tmakeRequest(ctx)\n\t\tSuccessfullyFinishedRequestEvent(span)\n\t\tspan.End()\n\t\ttime.Sleep(time.Duration(1) * time.Second)\n\t}\n}\n\n// makeRequest sends requests to the server using an OTEL HTTP transport which will instrument the requests with traces.\nfunc makeRequest(ctx context.Context) {\n\n\tdemoServerAddr, ok := os.LookupEnv(\"DEMO_SERVER_ENDPOINT\")\n\tif !ok {\n\t\tdemoServerAddr = \"http://0.0.0.0:7080/hello\"\n\t}\n\n\t// Trace an HTTP client by wrapping the transport\n\tclient := http.Client{\n\t\tTransport: otelhttp.NewTransport(http.DefaultTransport),\n\t}\n\n\t// Make sure we pass the context to the request to avoid broken traces.\n\treq, err := http.NewRequestWithContext(ctx, \"GET\", demoServerAddr, nil)\n\tif err != nil {\n\t\thandleErr(err, \"failed to http request\")\n\t}\n\n\t// All requests made with this client will create spans.\n\tres, err := client.Do(req)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tres.Body.Close()\n}\n\n// SuccessfullyFinishedRequestEvent adds an event to the span which is analogous with a log statement, but is included\n// in the trace structure and provides more context than a log statement.\nfunc SuccessfullyFinishedRequestEvent(span trace.Span, opts ...trace.EventOption) {\n\topts = append(opts, trace.WithAttributes(attribute.String(\"someKey\", \"someValue\")))\n\tspan.AddEvent(\"successfully finished request operation\", opts...)\n}\n\n// WithCorrelation adds span and trace IDs to a zap logger to enable better correlation between traces and logs.\nfunc WithCorrelation(span trace.Span, log *zap.Logger) *zap.Logger {\n\treturn log.With(\n\t\tzap.String(\"span_id\", convertTraceID(span.SpanContext().SpanID().String())),\n\t\tzap.String(\"trace_id\", convertTraceID(span.SpanContext().TraceID().String())),\n\t)\n}\n\nfunc convertTraceID(id string) string {\n\tif len(id) < 16 {\n\t\treturn \"\"\n\t}\n\tif len(id) > 16 {\n\t\tid = id[16:]\n\t}\n\tintValue, err := strconv.ParseUint(id, 16, 64)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn strconv.FormatUint(intValue, 10)\n}\n"
  },
  {
    "path": "chapter/9/tracing/docker-compose.yaml",
    "content": "version: \"2\"\nservices:\n\n  # Jaeger\n  jaeger-all-in-one:\n    image: jaegertracing/all-in-one:latest\n    ports:\n      - \"16686:16686\"\n      - \"14268\"\n      - \"14250\"\n\n  # Collector\n  otel-collector:\n    image: ${OTELCOL_IMG}\n    command: [\"--config=/etc/otel-collector-config.yaml\", \"${OTELCOL_ARGS}\"]\n    volumes:\n      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml\n    ports:\n      - \"1888:1888\"   # pprof extension\n      - \"13133:13133\" # health_check extension\n      - \"4317\"        # OTLP gRPC receiver\n      - \"55670:55679\" # zpages extension\n    depends_on:\n      - jaeger-all-in-one\n\n  demo-client:\n    build:\n      dockerfile: Dockerfile\n      context: ./client\n    environment:\n      - OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317\n      - DEMO_SERVER_ENDPOINT=http://demo-server:7080/hello\n    depends_on:\n      - demo-server\n\n  demo-server:\n    build:\n      dockerfile: Dockerfile\n      context: ./server\n    environment:\n      - OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317\n    ports:\n      - \"7080\"\n    depends_on:\n      - otel-collector\n"
  },
  {
    "path": "chapter/9/tracing/otel-collector-config.yaml",
    "content": "receivers:\n  otlp:\n    protocols:\n      grpc:\n\nexporters:\n  jaeger:\n    endpoint: jaeger-all-in-one:14250\n    tls:\n      insecure: true\n\nprocessors:\n  batch:\n\nservice:\n  pipelines:\n    traces:\n      receivers: [otlp]\n      processors: [batch]\n      exporters: [jaeger]\n\n"
  },
  {
    "path": "chapter/9/tracing/readme.md",
    "content": "# Tracing with OpenTelemetry and Jaeger\n\nTODO: fill in the walk through\n\n## Running this example\n- `docker-compose up -d`\n- Once started the client application will periodically send requests to the server. Distributed traces will be collected for the requests and responses, then exported for analysis in Jaeger. To view the traces in Jaeger, open http://localhost:16686.\n\nIf you see something like:\n```bash\ndocker-compose up -d\nTraceback (most recent call last):\n  File \"urllib3/connectionpool.py\", line 670, in urlopen\n  File \"urllib3/connectionpool.py\", line 392, in _make_request\n```\nThis indicates that you aren't running docker. Make sure you have docker installed and it is running.\n\n## Tearing down this example\n- `docker-compose down`\n\n## Influences / Credit\nThe code in this demo was heavily influenced from the example application in https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/examples/demo\nwhich carries the following license.\n```\n// Copyright The OpenTelemetry Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//       http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n```\n\nSee also: [OTEL License](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/10cfdaac1387b4df7a525c3050ce18ec8f8068be/LICENSE\n"
  },
  {
    "path": "chapter/9/tracing/server/Dockerfile",
    "content": "FROM golang:1.17\nCOPY . /usr/src/server/\nWORKDIR /usr/src/server/\nRUN go env -w GOPROXY=direct\nRUN go install ./main.go\nCMD [\"/go/bin/main\"]\n"
  },
  {
    "path": "chapter/9/tracing/server/go.mod",
    "content": "module github.com/PacktPublishing/Go-for-DevOps/chapter/9/tracing/demo/server\n\ngo 1.17\n\nrequire (\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0\n\tgo.opentelemetry.io/otel v1.3.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0\n\tgo.opentelemetry.io/otel/sdk v1.3.0\n\tgo.opentelemetry.io/otel/trace v1.3.0\n\tgoogle.golang.org/grpc v1.43.0\n)\n\nrequire (\n\tgithub.com/cenkalti/backoff/v4 v4.1.2 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/felixge/httpsnoop v1.0.2 // indirect\n\tgithub.com/go-logr/logr v1.2.1 // indirect\n\tgithub.com/go-logr/stdr v1.2.0 // indirect\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgithub.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 // indirect\n\tgo.opentelemetry.io/otel/internal/metric v0.26.0 // indirect\n\tgo.opentelemetry.io/otel/metric v0.26.0 // indirect\n\tgo.opentelemetry.io/proto/otlp v0.11.0 // indirect\n\tgolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect\n\tgolang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect\n\tgolang.org/x/text v0.3.3 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece // indirect\n\tgoogle.golang.org/protobuf v1.27.1 // indirect\n)\n"
  },
  {
    "path": "chapter/9/tracing/server/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=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=\ngithub.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\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/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\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/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.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=\ngithub.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=\ngithub.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=\ngithub.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/protobuf v1.2.0/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.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/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.5.0/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 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0 h1:hpEoMBvKLC6CqFZogJypr9IHwwSNF3ayEkNzD502QAM=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.28.0/go.mod h1:Ihno+mNBfZlT0Qot3XyRTdZ/9U/Cg2Pfgj75DTdIfq4=\ngo.opentelemetry.io/otel v1.3.0 h1:APxLf0eiBwLl+SOXiJJCVYzA1OOJNyAoV8C5RNRyy7Y=\ngo.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 h1:R/OBkMoGgfy2fLhs2QhkCI1w4HLEQX92GCcJB6SSdNk=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0 h1:giGm8w67Ja7amYNfYMdme7xSp2pIxThWopw8+QP51Yk=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0 h1:VQbUHoJqytHHSJ1OZodPH9tvZZSVzUHjPHpkO85sT6k=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY=\ngo.opentelemetry.io/otel/internal/metric v0.26.0 h1:dlrvawyd/A+X8Jp0EBT4wWEe4k5avYaXsXrBr4dbfnY=\ngo.opentelemetry.io/otel/internal/metric v0.26.0/go.mod h1:CbBP6AxKynRs3QCbhklyLUtpfzbqCLiafV9oY2Zj1Jk=\ngo.opentelemetry.io/otel/metric v0.26.0 h1:VaPYBTvA13h/FsiWfxa3yZnZEm15BhStD8JZQSA773M=\ngo.opentelemetry.io/otel/metric v0.26.0/go.mod h1:c6YL0fhRo4YVoNs6GoByzUgBp36hBL523rECoZA5UWg=\ngo.opentelemetry.io/otel/sdk v1.3.0 h1:3278edCoH89MEJ0Ky8WQXVmDQv3FX4ZJ3Pp+9fJreAI=\ngo.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=\ngo.opentelemetry.io/otel/trace v1.3.0 h1:doy8Hzb1RJ+I3yFhtDmwNc7tIyw1tNMOIsyPzp1NOGY=\ngo.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.opentelemetry.io/proto/otlp v0.11.0 h1:cLDgIBTf4lLOlztkhzAEdQsJ4Lj+i5Wc9k6Nn0K1VyU=\ngo.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ=\ngo.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=\ngo.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\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-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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\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-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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\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=\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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece h1:1YM0uhfumvoDu9sx8+RyWwTI63zoCQvI23IYFRlvte0=\ngoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM=\ngoogle.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\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/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/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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\n"
  },
  {
    "path": "chapter/9/tracing/server/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"math/rand\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\"\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc\"\n\t\"go.opentelemetry.io/otel/propagation\"\n\t\"go.opentelemetry.io/otel/sdk/resource\"\n\tsdktrace \"go.opentelemetry.io/otel/sdk/trace\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.4.0\"\n\t\"go.opentelemetry.io/otel/trace\"\n\t\"google.golang.org/grpc\"\n)\n\nvar rng = rand.New(rand.NewSource(time.Now().UnixNano()))\n\n// main initializes tracing provider and listens to requests at /hello returning \"Hello World!\" with\n// randomized latency.\nfunc main() {\n\tshutdown := initTraceProvider()\n\tdefer shutdown()\n\n\t// create a handler wrapped in OpenTelemetry instrumentation\n\thandler := handleRequestWithRandomSleep()\n\twrappedHandler := otelhttp.NewHandler(handler, \"/hello\")\n\n\t// serve up the wrapped handler\n\thttp.Handle(\"/hello\", wrappedHandler)\n\thttp.ListenAndServe(\":7080\", nil)\n}\n\n// handleRequestWithRandomSleep registers a request handler that will randomly sleep to induce artificial request latency.\nfunc handleRequestWithRandomSleep() http.HandlerFunc {\n\tcommonLabels := []attribute.KeyValue{\n\t\tattribute.String(\"server-attribute\", \"foo\"),\n\t}\n\n\treturn func(w http.ResponseWriter, req *http.Request) {\n\t\t//  random sleep to simulate latency\n\t\tvar sleep int64\n\t\tswitch modulus := time.Now().Unix() % 5; modulus {\n\t\tcase 0:\n\t\t\tsleep = rng.Int63n(2000)\n\t\tcase 1:\n\t\t\tsleep = rng.Int63n(15)\n\t\tcase 2:\n\t\t\tsleep = rng.Int63n(917)\n\t\tcase 3:\n\t\t\tsleep = rng.Int63n(87)\n\t\tcase 4:\n\t\t\tsleep = rng.Int63n(1173)\n\t\t}\n\t\ttime.Sleep(time.Duration(sleep) * time.Millisecond)\n\t\tctx := req.Context()\n\t\tspan := trace.SpanFromContext(ctx)\n\t\tspan.SetAttributes(commonLabels...)\n\t\tw.Write([]byte(\"Hello World\"))\n\t}\n}\n\n// initTraceProvider initializes an OTLP exporter, and configures the corresponding trace provider.\nfunc initTraceProvider() func() {\n\tctx := context.Background()\n\n\totelAgentAddr, ok := os.LookupEnv(\"OTEL_EXPORTER_OTLP_ENDPOINT\")\n\tif !ok {\n\t\totelAgentAddr = \"0.0.0.0:4317\"\n\t}\n\n\tcloseTraces := initTracer(ctx, otelAgentAddr)\n\n\treturn func() {\n\t\tdoneCtx, cancel := context.WithTimeout(ctx, time.Second)\n\t\tdefer cancel()\n\t\t// pushes any last exports to the receiver\n\t\tcloseTraces(doneCtx)\n\t}\n}\n\n// initTracer initializes an OTLP trace exporter and registers the trace provider with the global context\nfunc initTracer(ctx context.Context, otelAgentAddr string) func(context.Context) {\n\ttraceClient := otlptracegrpc.NewClient(\n\t\totlptracegrpc.WithInsecure(),\n\t\totlptracegrpc.WithEndpoint(otelAgentAddr),\n\t\totlptracegrpc.WithDialOption(grpc.WithBlock()))\n\ttraceExp, err := otlptrace.New(ctx, traceClient)\n\thandleErr(err, \"Failed to create the collector trace exporter\")\n\n\tres, err := resource.New(ctx,\n\t\tresource.WithFromEnv(),\n\t\tresource.WithProcess(),\n\t\tresource.WithTelemetrySDK(),\n\t\tresource.WithHost(),\n\t\tresource.WithAttributes(\n\t\t\t// the service name used to display traces in backends\n\t\t\tsemconv.ServiceNameKey.String(\"demo-server\"),\n\t\t),\n\t)\n\thandleErr(err, \"failed to create resource\")\n\n\tbsp := sdktrace.NewBatchSpanProcessor(traceExp)\n\ttracerProvider := sdktrace.NewTracerProvider(\n\t\tsdktrace.WithSampler(sdktrace.AlwaysSample()),\n\t\tsdktrace.WithResource(res),\n\t\tsdktrace.WithSpanProcessor(bsp),\n\t)\n\n\t// set global propagator to tracecontext (the default is no-op).\n\totel.SetTextMapPropagator(propagation.TraceContext{})\n\totel.SetTracerProvider(tracerProvider)\n\n\treturn func(doneCtx context.Context) {\n\t\tif err := traceExp.Shutdown(doneCtx); err != nil {\n\t\t\totel.Handle(err)\n\t\t}\n\t}\n}\n\nfunc handleErr(err error, message string) {\n\tif err != nil {\n\t\tlog.Fatalf(\"%s: %v\", message, err)\n\t}\n}\n"
  },
  {
    "path": "chapter/build_check.sh",
    "content": "#!/bin/bash\n\n# Running this file will recursively dive into every directory that has a .go file\n# and run \"go test\" in that directory. \n# Note: this will fail on the k8 stuff, as it has a complicated test setup.\n\nroot=`pwd`\n\nfor d in `find . -type d -print`; do\n\t#echo \"found $d\";\n\tcd \"$root/$d\";\n\n\tgoDir=false;\n\tfor file in `find . -type f -name '*.go' -maxdepth 1 -print`; do\n\t\tgoDir=true;\n\t\tbreak;\n\tdone;\n\n        if $goDir\n        then\n                echo \"Has Go files: $d\";\n\t\t#go build -o /tmp/\n\t\tgo test\n        fi\ndone;\n\n#go build -o /tmp/\n"
  },
  {
    "path": "chapter/mod_tidy.sh",
    "content": "#!/bin/bash\n\n# This will find every directory with a go.mod file and run go mod tidy on it.\n\nroot=`pwd`\n\nfor d in `find . -type d -print`; do\n\tcd \"$root/$d\";\n\n\tfound=false;\n\tfor file in `find . -type f -name 'go.mod' -maxdepth 1 -print`; do\n\t\tfound=true;\n\t\tbreak;\n\tdone;\n\n        if $found\n        then\n                echo \"Has go.mod file: $d\";\n\t\tgo mod tidy\n        fi\ndone;\n"
  },
  {
    "path": "chapter/rename.sh",
    "content": "\n#!/bin/bash\n\n# By setting variables old and new, this script can be run to update the name\n# of a chapter path for our imports.\n\nold=\"chaos\"\nnew=\"16\"\nmyArray=(\"*.go\" \"go.mod\" \"DOCKERFILE\" \"Dockerfile\" \"Makefile\" \"*.yml\" \"*.yaml\" \"*.json\")\n\nfor name in ${myArray[@]}; do\n\tfind . -type f -name \"$name\" -exec sed -i '' \"s/chapter\\/$old/chapter\\/$new/g\" {} \\;\ndone\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/PacktPublishing/Go-for-DevOps\n\ngo 1.18\n\nreplace github.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore => ./chapter/11/petstore\n\nrequire (\n\tgithub.com/360EntSecGroup-Skylar/excelize v1.4.1\n\tgithub.com/PacktPublishing/Go-for-DevOps/chapter/11/petstore v0.0.0-00010101000000-000000000000\n\tgithub.com/aelsabbahy/goss v0.3.16\n\tgithub.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8\n\tgithub.com/cenkalti/backoff v2.2.1+incompatible\n\tgithub.com/coreos/go-systemd/v22 v22.3.2\n\tgithub.com/fatih/color v1.13.0\n\tgithub.com/gogo/protobuf v1.3.2\n\tgithub.com/google/goexpect v0.0.0-20210430020637-ab937bf7fd6f\n\tgithub.com/google/uuid v1.3.0\n\tgithub.com/gopherfs/fs v0.0.0-20220204202500-4538e04c7abb\n\tgithub.com/hashicorp/hcl/v2 v2.11.1\n\tgithub.com/hashicorp/packer-plugin-sdk v0.2.11\n\tgithub.com/inancgumus/screen v0.0.0-20190314163918-06e984b86ed3\n\tgithub.com/johnsiilver/serveonssh v0.0.0-20211102170212-8f457c0359be\n\tgithub.com/joho/godotenv v1.4.0\n\tgithub.com/olekukonko/tablewriter v0.0.5\n\tgithub.com/pkg/sftp v1.13.4\n\tgithub.com/prometheus/client_golang v1.12.1\n\tgithub.com/prometheus/common v0.33.0\n\tgithub.com/rodaine/table v1.0.1\n\tgithub.com/slack-go/slack v0.10.2\n\tgithub.com/sony/gobreaker v0.5.0\n\tgithub.com/spf13/cobra v1.4.0\n\tgithub.com/spf13/pflag v1.0.5\n\tgithub.com/spf13/viper v1.10.1\n\tgithub.com/xuri/excelize/v2 v2.6.0\n\tgithub.com/zclconf/go-cty v1.10.0\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.31.0\n\tgo.opentelemetry.io/otel v1.6.3\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.6.3\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.6.3\n\tgo.opentelemetry.io/otel/sdk v1.6.3\n\tgo.opentelemetry.io/otel/trace v1.6.3\n\tgolang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4\n\tgoogle.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac\n\tgoogle.golang.org/grpc v1.45.0\n\tgoogle.golang.org/protobuf v1.28.0\n\tgopkg.in/yaml.v2 v2.4.0\n\tinet.af/netaddr v0.0.0-20211027220019-c74959edd3b6\n)\n\nrequire (\n\tgithub.com/Masterminds/goutils v1.1.1 // indirect\n\tgithub.com/Masterminds/semver v1.5.0 // indirect\n\tgithub.com/Masterminds/sprig v2.22.0+incompatible // indirect\n\tgithub.com/achanda/go-sysctl v0.0.0-20160222034550-6be7678c45d2 // indirect\n\tgithub.com/aelsabbahy/GOnetstat v0.0.0-20160428114218-edf89f784e08 // indirect\n\tgithub.com/aelsabbahy/go-ps v0.0.0-20201009164808-61c449472dcf // indirect\n\tgithub.com/agext/levenshtein v1.2.3 // indirect\n\tgithub.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect\n\tgithub.com/armon/go-metrics v0.3.10 // indirect\n\tgithub.com/aws/aws-sdk-go v1.40.34 // indirect\n\tgithub.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect\n\tgithub.com/blang/semver v3.5.1+incompatible // indirect\n\tgithub.com/cenkalti/backoff/v3 v3.2.2 // indirect\n\tgithub.com/cenkalti/backoff/v4 v4.1.2 // indirect\n\tgithub.com/cheekybits/genny v1.0.0 // indirect\n\tgithub.com/docker/docker v1.13.1 // indirect\n\tgithub.com/felixge/httpsnoop v1.0.2 // indirect\n\tgithub.com/fsnotify/fsnotify v1.5.1 // indirect\n\tgithub.com/go-logr/logr v1.2.3 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/godbus/dbus/v5 v5.0.4 // indirect\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgithub.com/golang/snappy v0.0.4 // indirect\n\tgithub.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f // indirect\n\tgithub.com/gorilla/websocket v1.4.2 // indirect\n\tgithub.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect\n\tgithub.com/hashicorp/consul/api v1.12.0 // indirect\n\tgithub.com/hashicorp/errwrap v1.1.0 // indirect\n\tgithub.com/hashicorp/go-cleanhttp v0.5.2 // indirect\n\tgithub.com/hashicorp/go-getter/v2 v2.0.0 // indirect\n\tgithub.com/hashicorp/go-hclog v1.0.0 // indirect\n\tgithub.com/hashicorp/go-immutable-radix v1.3.1 // indirect\n\tgithub.com/hashicorp/go-multierror v1.1.1 // indirect\n\tgithub.com/hashicorp/go-retryablehttp v0.7.0 // indirect\n\tgithub.com/hashicorp/go-rootcerts v1.0.2 // indirect\n\tgithub.com/hashicorp/go-safetemp v1.0.0 // indirect\n\tgithub.com/hashicorp/go-sockaddr v1.0.2 // indirect\n\tgithub.com/hashicorp/go-version v1.3.0 // indirect\n\tgithub.com/hashicorp/golang-lru v0.5.4 // indirect\n\tgithub.com/hashicorp/hcl v1.0.0 // indirect\n\tgithub.com/hashicorp/serf v0.9.6 // indirect\n\tgithub.com/hashicorp/vault/api v1.1.1 // indirect\n\tgithub.com/hashicorp/vault/sdk v0.2.1 // indirect\n\tgithub.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493 // indirect\n\tgithub.com/huandu/xstrings v1.3.2 // indirect\n\tgithub.com/imdario/mergo v0.3.12 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.0.0 // indirect\n\tgithub.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect\n\tgithub.com/jmespath/go-jmespath v0.4.0 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/kr/fs v0.1.0 // indirect\n\tgithub.com/magiconair/properties v1.8.5 // indirect\n\tgithub.com/mattn/go-colorable v0.1.12 // indirect\n\tgithub.com/mattn/go-isatty v0.0.14 // indirect\n\tgithub.com/mattn/go-runewidth v0.0.9 // indirect\n\tgithub.com/miekg/dns v1.1.41 // indirect\n\tgithub.com/mitchellh/copystructure v1.0.0 // indirect\n\tgithub.com/mitchellh/go-homedir v1.1.0 // indirect\n\tgithub.com/mitchellh/go-testing-interface v1.14.1 // indirect\n\tgithub.com/mitchellh/go-wordwrap v1.0.1 // indirect\n\tgithub.com/mitchellh/iochan v1.0.0 // indirect\n\tgithub.com/mitchellh/mapstructure v1.4.3 // indirect\n\tgithub.com/mitchellh/reflectwalk v1.0.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/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect\n\tgithub.com/oleiade/reflections v0.0.0-20160817071559-0e86b3c98b2f // indirect\n\tgithub.com/onsi/gomega v1.15.0 // indirect\n\tgithub.com/opencontainers/runc v0.1.1 // indirect\n\tgithub.com/patrickmn/go-cache v2.1.0+incompatible // indirect\n\tgithub.com/pelletier/go-toml v1.9.4 // indirect\n\tgithub.com/pierrec/lz4 v2.6.1+incompatible // indirect\n\tgithub.com/richardlehane/mscfb v1.0.4 // indirect\n\tgithub.com/richardlehane/msoleps v1.0.1 // indirect\n\tgithub.com/ryanuber/go-glob v1.0.0 // indirect\n\tgithub.com/spf13/afero v1.6.0 // indirect\n\tgithub.com/spf13/cast v1.4.1 // indirect\n\tgithub.com/spf13/jwalterweatherman v1.1.0 // indirect\n\tgithub.com/subosito/gotenv v1.2.0 // indirect\n\tgithub.com/ugorji/go/codec v1.2.6 // indirect\n\tgithub.com/ulikunitz/xz v0.5.10 // indirect\n\tgithub.com/xuri/efp v0.0.0-20220407160117-ad0f7a785be8 // indirect\n\tgithub.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.6.3 // indirect\n\tgo.opentelemetry.io/otel/metric v0.28.0 // indirect\n\tgo.opentelemetry.io/proto/otlp v0.15.0 // indirect\n\tgo4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect\n\tgo4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 // indirect\n\tgolang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3 // indirect\n\tgolang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect\n\tgolang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect\n\tgolang.org/x/text v0.3.7 // indirect\n\tgolang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect\n\tgopkg.in/ini.v1 v1.66.2 // indirect\n\tgopkg.in/square/go-jose.v2 v2.6.0 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=\ncloud.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/360EntSecGroup-Skylar/excelize v1.4.1 h1:l55mJb6rkkaUzOpSsgEeKYtS6/0gHwBYyfo5Jcjv/Ks=\ngithub.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=\ngithub.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=\ngithub.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck=\ngithub.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=\ngithub.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.16/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A=\ngithub.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=\ngithub.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=\ngithub.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=\ngithub.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=\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/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=\ngithub.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=\ngithub.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=\ngithub.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=\ngithub.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=\ngithub.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=\ngithub.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=\ngithub.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=\ngithub.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=\ngithub.com/achanda/go-sysctl v0.0.0-20160222034550-6be7678c45d2 h1:NYoPVh1XuUB5VBWLXRKoqzQhl4bajIxh+XuURbJ0uwc=\ngithub.com/achanda/go-sysctl v0.0.0-20160222034550-6be7678c45d2/go.mod h1:DCNKSpXhum14Y258jSbRmJvcesbzEdBPincz7yJUx3k=\ngithub.com/aelsabbahy/GOnetstat v0.0.0-20160428114218-edf89f784e08 h1:oD15ssIOuFLi64zhkPRsaIDvhx4PeZb2QdQoR/wKY2g=\ngithub.com/aelsabbahy/GOnetstat v0.0.0-20160428114218-edf89f784e08/go.mod h1:FETZSu2VGNDJbGfeRExaz/SNbX0TTaqJEMo1yvsKoZ8=\ngithub.com/aelsabbahy/go-ps v0.0.0-20201009164808-61c449472dcf h1:KyjxaqJO0pHF7Clre644OiJ5s235JVRsz6ioDkoQ96s=\ngithub.com/aelsabbahy/go-ps v0.0.0-20201009164808-61c449472dcf/go.mod h1:70tSBushy/POz6cCR294bKno4BNAC7XWVdkkxWQ1N6E=\ngithub.com/aelsabbahy/goss v0.3.16 h1:L3jhYJWx1ErwcHXcNOP3yXaqm7Q+f6ZwxIG34RFz+qU=\ngithub.com/aelsabbahy/goss v0.3.16/go.mod h1:XX1OBiqcx7Dd7b1YaG/RRm9pLVT5uomamhqy3bluER8=\ngithub.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=\ngithub.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=\ngithub.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=\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/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 h1:ZSTrOEhiM5J5RFxEaFvMZVEAM1KvT1YzbEOwB2EAGjA=\ngithub.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=\ngithub.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=\ngithub.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=\ngithub.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=\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.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs=\ngithub.com/armon/go-metrics v0.3.3/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=\ngithub.com/armon/go-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8Uo=\ngithub.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=\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.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=\ngithub.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=\ngithub.com/aws/aws-sdk-go v1.40.34 h1:SBYmodndE2d4AYucuuJnOXk4MD1SFbucoIdpwKVKeSA=\ngithub.com/aws/aws-sdk-go v1.40.34/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=\ngithub.com/beeker1121/goque v2.1.0+incompatible/go.mod h1:L6dOWBhDOnxUVQsb0wkLve0VCnt2xJW/MI8pdRX4ANw=\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/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=\ngithub.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=\ngithub.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=\ngithub.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 h1:SjZ2GvvOononHOpK84APFuMvxqsk3tEIaKH/z4Rpu3g=\ngithub.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8/go.mod h1:uEyr4WpAH4hio6LFriaPkL938XnrvLpNPmQHBdrmbIE=\ngithub.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=\ngithub.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=\ngithub.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=\ngithub.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=\ngithub.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=\ngithub.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=\ngithub.com/cenkalti/backoff/v4 v4.1.2/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/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=\ngithub.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=\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/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/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=\ngithub.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=\ngithub.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=\ngithub.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=\ngithub.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=\ngithub.com/containerd/continuity v0.0.0-20200709052629-daa8e1ccc0bc/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo=\ngithub.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=\ngithub.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=\ngithub.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=\ngithub.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\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/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=\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/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=\ngithub.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=\ngithub.com/docker/docker v1.4.2-0.20200319182547-c7ad2b866182/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=\ngithub.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=\ngithub.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=\ngithub.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=\ngithub.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=\ngithub.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=\ngithub.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=\ngithub.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=\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.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=\ngithub.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\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.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=\ngithub.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=\ngithub.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=\ngithub.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=\ngithub.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=\ngithub.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY=\ngithub.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=\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/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=\ngithub.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=\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/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=\ngithub.com/go-ldap/ldap/v3 v3.1.3/go.mod h1:3rbOH3jRS2u6jg2rJnKAMLE/xQyCKIveG2Sa/Cohzb8=\ngithub.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q=\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/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=\ngithub.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=\ngithub.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=\ngithub.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=\ngithub.com/go-redis/redis/v8 v8.11.3/go.mod h1:xNJ9xDG09FsIPwh3bWdk+0oDWHbtF9rPN0F/oD9XeKc=\ngithub.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\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-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=\ngithub.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=\ngithub.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=\ngithub.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=\ngithub.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=\ngithub.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=\ngithub.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=\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.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=\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/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=\ngithub.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=\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/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/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\ngithub.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\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/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=\ngithub.com/golang/snappy v0.0.4/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.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/goexpect v0.0.0-20210430020637-ab937bf7fd6f h1:7MmqygqdeJtziBUpm4Z9ThROFZUaVGaePMfcDnluf1E=\ngithub.com/google/goexpect v0.0.0-20210430020637-ab937bf7fd6f/go.mod h1:n1ej5+FqyEytMt/mugVDZLIiqTMO+vsrgY+kM6ohzN0=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f h1:5CjVwnuUcp5adK4gmY6i72gpVFVnZDP2h5TmPScB6u4=\ngithub.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4=\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.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=\ngithub.com/google/uuid v1.3.0/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/gopherfs/fs v0.0.0-20220204202500-4538e04c7abb h1:Jm2ojAu8QLI8kjS/+qIC++KlowGTb1sk9RreqbiuJAU=\ngithub.com/gopherfs/fs v0.0.0-20220204202500-4538e04c7abb/go.mod h1:37tq52MJx44jOSzHph17/OxzgtVTAOnX3vbrsJSleC8=\ngithub.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=\ngithub.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=\ngithub.com/hashicorp/consul/api v1.12.0 h1:k3y1FYv6nuKyNTqj6w9gXOx5r5CfLj/k/euUeBXj1OY=\ngithub.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0=\ngithub.com/hashicorp/consul/sdk v0.8.0 h1:OJtKBtEjboEZvG6AOUdh4Z1Zbyu0WcxQ0qatRrZHTVU=\ngithub.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=\ngithub.com/hashicorp/errwrap v1.1.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-getter/v2 v2.0.0 h1:wamdcQazMBZK6VwUo3HAOWLkcOJBWBoXPKfmf7/S17w=\ngithub.com/hashicorp/go-getter/v2 v2.0.0/go.mod h1:w65fE5glbccYjndAuj1kA5lnVBGZYEaH0e5qA1kpIks=\ngithub.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=\ngithub.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=\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 v0.16.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=\ngithub.com/hashicorp/go-hclog v1.0.0 h1:bkKf0BeBXcSYa7f5Fyi9gMuQ8gNsxeiNpZjR6VxNZeo=\ngithub.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-immutable-radix v1.1.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-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g=\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-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-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=\ngithub.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=\ngithub.com/hashicorp/go-retryablehttp v0.6.2/go.mod h1:gEx6HMUGxYYhJScX7W1Il64m6cc2C1mDaW3NQ9sY1FY=\ngithub.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=\ngithub.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4=\ngithub.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=\ngithub.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=\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-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=\ngithub.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=\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/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=\ngithub.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=\ngithub.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw=\ngithub.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=\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.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=\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/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/hcl/v2 v2.11.1 h1:yTyWcXcm9XB0TEkyU/JCRU6rYy4K+mgLtzn2wlrJbcc=\ngithub.com/hashicorp/hcl/v2 v2.11.1/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg=\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 h1:8+567mCcFDnS5ADl7lrpxPMWiFCElyUEeW0gtj34fMA=\ngithub.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=\ngithub.com/hashicorp/packer-plugin-sdk v0.2.11 h1:FsL2oOfLJmXC9F6W9eMFe0eUi3+ggFuqkLGX41fyPE0=\ngithub.com/hashicorp/packer-plugin-sdk v0.2.11/go.mod h1:DI8REf9TEIcVkYPErI/nedo6zS2h81ze7sKuc/mIlTE=\ngithub.com/hashicorp/serf v0.9.6 h1:uuEX1kLR6aoda1TBttmJQKDLZE1Ob7KN0NPdE7EtCDc=\ngithub.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=\ngithub.com/hashicorp/vault/api v1.0.5-0.20200519221902-385fac77e20f/go.mod h1:euTFbi2YJgwcju3imEt919lhJKF68nN1cQPq3aA+kBE=\ngithub.com/hashicorp/vault/api v1.1.1 h1:907ld+Z9cALyvbZK2qUX9cLwvSaEQsMVQB3x2KE8+AI=\ngithub.com/hashicorp/vault/api v1.1.1/go.mod h1:29UXcn/1cLOPHQNMWA7bCz2By4PSd0VKPAydKXS5yN0=\ngithub.com/hashicorp/vault/sdk v0.1.14-0.20200519221530-14615acda45f/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10=\ngithub.com/hashicorp/vault/sdk v0.2.1 h1:S4O6Iv/dyKlE9AUTXGa7VOvZmsCvg36toPKgV4f2P4M=\ngithub.com/hashicorp/vault/sdk v0.2.1/go.mod h1:WfUiO1vYzfBkz1TmoE4ZGU7HD0T0Cl/rZwaxjBkgN4U=\ngithub.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=\ngithub.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493 h1:brI5vBRUlAlM34VFmnLPwjnCL/FxAJp9XvOdX6Zt+XE=\ngithub.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=\ngithub.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=\ngithub.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=\ngithub.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=\ngithub.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=\ngithub.com/inancgumus/screen v0.0.0-20190314163918-06e984b86ed3 h1:fO9A67/izFYFYky7l1pDP5Dr0BTCRkaQJUG6Jm5ehsk=\ngithub.com/inancgumus/screen v0.0.0-20190314163918-06e984b86ed3/go.mod h1:Ey4uAp+LvIl+s5jRbOHLcZpUDnkjLBROl15fZLwPlTM=\ngithub.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=\ngithub.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=\ngithub.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4=\ngithub.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=\ngithub.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=\ngithub.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=\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/johnsiilver/boutique v0.1.1-beta.1/go.mod h1:+CckOspS8vOgYT91+H1+7nt4BeycsX2B/AISSEkTz0g=\ngithub.com/johnsiilver/golib v1.1.1/go.mod h1:z55S8DxidlekNqN9/IQ/Q6uhkaqKI6AIEulnaYEcOjk=\ngithub.com/johnsiilver/serveonssh v0.0.0-20211102170212-8f457c0359be h1:cDJFabzuhMhvnWDEhRKSyzwitYohw8CRvnQ38M5PcE8=\ngithub.com/johnsiilver/serveonssh v0.0.0-20211102170212-8f457c0359be/go.mod h1:djRrNyRIA6c1mmVq0ULNF8yyKKI3YQj34wy8kykBjaY=\ngithub.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=\ngithub.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=\ngithub.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=\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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=\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/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/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=\ngithub.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=\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 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=\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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=\ngithub.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=\ngithub.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=\ngithub.com/lukechampine/freeze v0.0.0-20160818180733-f514e08ae5a0/go.mod h1:kHf6qlhSQAjGo6pMSDgbSc78BG+K/cNjj/Pqw5bT2oQ=\ngithub.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=\ngithub.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=\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.8/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 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=\ngithub.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=\ngithub.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=\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 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=\ngithub.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=\ngithub.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=\ngithub.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=\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.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=\ngithub.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY=\ngithub.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=\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/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=\ngithub.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\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-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=\ngithub.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=\ngithub.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=\ngithub.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=\ngithub.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=\ngithub.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=\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/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=\ngithub.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=\ngithub.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=\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 h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=\ngithub.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=\ngithub.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=\ngithub.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=\ngithub.com/oleiade/reflections v0.0.0-20160817071559-0e86b3c98b2f h1:I6mXuorHlvwNDFelz7a+j0HaGYSzX7+Gq60DqLVypfc=\ngithub.com/oleiade/reflections v0.0.0-20160817071559-0e86b3c98b2f/go.mod h1:RbATFBbKYkVdqmSFtx13Bb/tVhR0lgOBXunWTZKeL4w=\ngithub.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=\ngithub.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=\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.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=\ngithub.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=\ngithub.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=\ngithub.com/onsi/gomega v1.7.0/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.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuKHUCQ=\ngithub.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU=\ngithub.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=\ngithub.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=\ngithub.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=\ngithub.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=\ngithub.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=\ngithub.com/opencontainers/runc v0.0.0-20161107232042-8779fa57eb4a/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=\ngithub.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=\ngithub.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=\ngithub.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=\ngithub.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=\ngithub.com/panjf2000/ants/v2 v2.4.3/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A=\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/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=\ngithub.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=\ngithub.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=\ngithub.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=\ngithub.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=\ngithub.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9/go.mod h1:x3N5drFsm2uilKKuuYo6LdyD8vZAW55sH/9w+pbo1sw=\ngithub.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=\ngithub.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=\ngithub.com/pierrec/lz4 v2.6.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=\ngithub.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=\ngithub.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/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/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=\ngithub.com/pkg/sftp v1.13.4 h1:Lb0RYJCmgUcBgZosfoi9Y9sbl6+LJgOIgk/2Y4YjMFg=\ngithub.com/pkg/sftp v1.13.4/go.mod h1:LzqnAvaD5TWeNBsZpfKxSYn1MbjWwOsCIAFFJbpIsK8=\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 v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=\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.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=\ngithub.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=\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.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\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.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=\ngithub.com/prometheus/common v0.33.0 h1:rHgav/0a6+uYgGdNt3jwz8FNSesO/Hsang3O0T9A5SE=\ngithub.com/prometheus/common v0.33.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE=\ngithub.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/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/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=\ngithub.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=\ngithub.com/richardlehane/msoleps v1.0.1 h1:RfrALnSNXzmXLbGct/P2b4xkFz4e8Gmj/0Vj9M9xC1o=\ngithub.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=\ngithub.com/rodaine/table v1.0.1 h1:U/VwCnUxlVYxw8+NJiLIuCxA/xa6jL38MY3FYysVWWQ=\ngithub.com/rodaine/table v1.0.1/go.mod h1:UVEtfBsflpeEcD56nF4F5AocNFta0ZuolpSVdPtlmP4=\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/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\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/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=\ngithub.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=\ngithub.com/schollz/peerdiscovery v1.6.6/go.mod h1:RLoWlEF1cJbSZhZUdBLCfwbW/4eWgFtZ6WYHcgKT9/8=\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/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/shirou/gopsutil v3.20.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=\ngithub.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=\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/slack-go/slack v0.10.2 h1:KMN/h2sgUninHXvQI8PrR/PHBUuWp2NPvz2Kr66tki4=\ngithub.com/slack-go/slack v0.10.2/go.mod h1:5FLdBRv7VW/d9EBxx/eEktOptWygbA9K2QK/KW7ds1s=\ngithub.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg=\ngithub.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=\ngithub.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=\ngithub.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=\ngithub.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=\ngithub.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=\ngithub.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=\ngithub.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=\ngithub.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=\ngithub.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/viper v1.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk=\ngithub.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU=\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.2.3-0.20181224173747-660f15d67dbb/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.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=\ngithub.com/tjarratt/babble v0.0.0-20191209142150-eecdf8c2339d/go.mod h1:O5hBrCGqzfb+8WyY8ico2AyQau7XQwAfEQeEQ5/5V9E=\ngithub.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=\ngithub.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=\ngithub.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=\ngithub.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=\ngithub.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=\ngithub.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=\ngithub.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=\ngithub.com/urfave/cli v0.0.0-20161102131801-d86a009f5e13/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=\ngithub.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=\ngithub.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=\ngithub.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=\ngithub.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=\ngithub.com/xuri/efp v0.0.0-20220407160117-ad0f7a785be8 h1:3X7aE0iLKJ5j+tz58BpvIZkXNV7Yq4jC93Z/rbN2Fxk=\ngithub.com/xuri/efp v0.0.0-20220407160117-ad0f7a785be8/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=\ngithub.com/xuri/excelize/v2 v2.6.0 h1:m/aXAzSAqxgt74Nfd+sNzpzVKhTGl7+S9nbG4A57mF4=\ngithub.com/xuri/excelize/v2 v2.6.0/go.mod h1:Q1YetlHesXEKwGFfeJn7PfEZz2IvHb6wdOeYjBxVcVs=\ngithub.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 h1:OAmKAfT06//esDdpi/DZ8Qsdt4+M5+ltca05dA5bG2M=\ngithub.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=\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=\ngithub.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=\ngithub.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=\ngithub.com/zclconf/go-cty v1.10.0 h1:mp9ZXQeIcN8kAwuqorjH+Q+njbJKjLrvB2yIh4q7U+0=\ngithub.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=\ngithub.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=\ngithub.com/ziutek/telnet v0.0.0-20180329124119-c3b780dc415b/go.mod h1:IZpXDfkJ6tWD3PhBK5YzgQT+xJWh7OsdwiG8hA2MkO4=\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.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.31.0 h1:woM+Mb4d0A+Dxa3rYPenSN5ZeS9qHUvE8rlObiLRXTY=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.31.0/go.mod h1:PFmBsWbldL1kiWZk9+0LBZz2brhByaGsvp6pRICMlPE=\ngo.opentelemetry.io/otel v1.6.0/go.mod h1:bfJD2DZVw0LBxghOTlgnlI0CV3hLDu9XF/QKOUXMTQQ=\ngo.opentelemetry.io/otel v1.6.1/go.mod h1:blzUabWHkX6LJewxvadmzafgh/wnvBSDBdOuwkAtrWQ=\ngo.opentelemetry.io/otel v1.6.3 h1:FLOfo8f9JzFVFVyU+MSRJc2HdEAXQgm7pIv2uFKRSZE=\ngo.opentelemetry.io/otel v1.6.3/go.mod h1:7BgNga5fNlF/iZjG06hM3yofffp0ofKCDwSXx1GC4dI=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.6.3 h1:nAmg1WgsUXoXf46dJG9eS/AzOcvkCTK4xJSUYpWyHYg=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.6.3/go.mod h1:NEu79Xo32iVb+0gVNV8PMd7GoWqnyDXRlj04yFjqz40=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.6.3 h1:4/UjHWMVVc5VwX/KAtqJOHErKigMCH8NexChMuanb/o=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.6.3/go.mod h1:UJmXdiVVBaZ63umRUTwJuCMAV//GCMvDiQwn703/GoY=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.6.3 h1:leYDq5psbM3K4QNcZ2juCj30LjUnvxjuYQj1mkGjXFM=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.6.3/go.mod h1:ycItY/esVj8c0dKgYTOztTERXtPzcfDU/0o8EdwCjoA=\ngo.opentelemetry.io/otel/metric v0.28.0 h1:o5YNh+jxACMODoAo1bI7OES0RUW4jAMae0Vgs2etWAQ=\ngo.opentelemetry.io/otel/metric v0.28.0/go.mod h1:TrzsfQAmQaB1PDcdhBauLMk7nyyg9hm+GoQq/ekE9Iw=\ngo.opentelemetry.io/otel/sdk v1.6.3 h1:prSHYdwCQOX5DrsEzxowH3nLhoAzEBdZhvrR79scfLs=\ngo.opentelemetry.io/otel/sdk v1.6.3/go.mod h1:A4iWF7HTXa+GWL/AaqESz28VuSBIcZ+0CV+IzJ5NMiQ=\ngo.opentelemetry.io/otel/trace v1.6.0/go.mod h1:qs7BrU5cZ8dXQHBGxHMOxwME/27YH2qEp4/+tZLLwJE=\ngo.opentelemetry.io/otel/trace v1.6.1/go.mod h1:RkFRM1m0puWIq10oxImnGEduNBzxiN7TXluRBtE+5j0=\ngo.opentelemetry.io/otel/trace v1.6.3 h1:IqN4L+5b0mPNjdXIiZ90Ni4Bl5BRkDQywePLWemd9bc=\ngo.opentelemetry.io/otel/trace v1.6.3/go.mod h1:GNJQusJlUgZl9/TQBPKU/Y/ty+0iVB5fjhKeJGZPGFs=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.opentelemetry.io/proto/otlp v0.15.0 h1:h0bKrvdrT/9sBwEJ6iWUqT/N/xPcS66bL4u3isneJ6w=\ngo.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=\ngo.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE=\ngo4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA=\ngo4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 h1:Tx9kY6yUkLge/pFG7IEMwDZy6CS2ajFc9TvQdPCW0uA=\ngo4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=\ngolang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\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-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=\ngolang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\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-20190820162420-60c769a6c586/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-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=\ngolang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=\ngolang.org/x/crypto v0.0.0-20220408190544-5352b0902921/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=\ngolang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/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/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\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/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=\ngolang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=\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/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-20180811021610-c39426892332/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-20181201002055-351d144fa1fc/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-20190813141303-74dc4d7220e7/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-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191112182307-2180aed22343/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-20200602114024-627f9648deb9/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-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\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-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\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-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3 h1:EN5+DfgmRMvRUrMGERW2gQl3Vc+Z7ZMnI/xdEpPSf0c=\ngolang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\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/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg=\ngolang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=\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-20190129075346-302c3dd5f1cc/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-20190403152447-81d4e9dc473e/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-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/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-20190916202348-b4ddaad3f8a3/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-20191112214154-59a1497f0cea/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-20200124204421-9fbb57f87de9/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-20200602225109-6fdc65e7d980/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-20200828194041-157a740278f4/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-20210119212857-b64e53b001e4/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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/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-20210511113859-b0526f3d8744/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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=\ngolang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\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-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=\ngolang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/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-20181030221726-6c7e314b6563/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-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-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-20190624222133-a101b041ded4/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-20191029041327-9cc4af7d6b2c/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-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\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.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=\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=\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/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=\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-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac h1:qSNTkEN+L2mvWcLgJOR+8bdHX9rN/IdU3A1Ghpfb1Rg=\ngoogle.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=\ngoogle.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=\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.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.23.1/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.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=\ngoogle.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=\ngoogle.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=\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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=\ngoogle.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=\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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\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/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=\ngopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=\ngopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=\ngopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=\ngopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=\ngopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\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.7/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 h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=\ngotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=\ngotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=\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=\ninet.af/netaddr v0.0.0-20211027220019-c74959edd3b6 h1:acCzuUSQ79tGsM/O50VRFySfMm19IoMKL+sZztZkCxw=\ninet.af/netaddr v0.0.0-20211027220019-c74959edd3b6/go.mod h1:y3MGhcFMlh0KZPMuXXow8mpjxxAk3yoDNsp4cQz54i8=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\n"
  }
]