├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .prettierrc.js ├── CHANGELOG.md ├── LICENSE ├── Magefile.go ├── README.md ├── go.mod ├── go.sum ├── jest.config.js ├── package.json ├── pkg ├── datasource │ ├── client.go │ ├── clusters.go │ ├── databases.go │ ├── datasource.go │ ├── disks.go │ ├── mongos.go │ ├── projects.go │ └── resource_handlers.go ├── dfutil │ └── framer.go ├── errors │ └── errors.go ├── httputil │ └── error.go ├── main.go ├── models │ ├── clusters.go │ ├── credentials.go │ ├── databases.go │ ├── disks.go │ ├── mongos.go │ ├── projects.go │ └── query.go └── plugin │ ├── datasource.go │ ├── instance.go │ ├── plugin.go │ ├── query_database_measurements.go │ ├── query_disk_measurements.go │ ├── query_process_measurements.go │ ├── resource_handlers.go │ └── server.go ├── src ├── ConfigEditor.tsx ├── QueryEditor.tsx ├── datasource.ts ├── img │ ├── logo.svg │ └── screenshots │ │ ├── datasource_list.png │ │ ├── datasource_setup.png │ │ ├── query_example.png │ │ └── query_setup.png ├── module.ts ├── plugin.json └── types.ts ├── tsconfig.json └── yarn.lock /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Setup Node.js environment 17 | uses: actions/setup-node@v2.1.2 18 | with: 19 | node-version: "14.x" 20 | 21 | - name: Get yarn cache directory path 22 | id: yarn-cache-dir-path 23 | run: echo "::set-output name=dir::$(yarn cache dir)" 24 | 25 | - name: Cache yarn cache 26 | uses: actions/cache@v2 27 | id: cache-yarn-cache 28 | with: 29 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 30 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 31 | restore-keys: | 32 | ${{ runner.os }}-yarn- 33 | 34 | - name: Cache node_modules 35 | id: cache-node-modules 36 | uses: actions/cache@v2 37 | with: 38 | path: node_modules 39 | key: ${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/yarn.lock') }} 40 | restore-keys: | 41 | ${{ runner.os }}-${{ matrix.node-version }}-nodemodules- 42 | 43 | - name: Install dependencies 44 | run: yarn install --frozen-lockfile 45 | 46 | - name: Build and test frontend 47 | run: yarn build 48 | 49 | - name: Check for backend 50 | id: check-for-backend 51 | run: | 52 | if [ -f "Magefile.go" ] 53 | then 54 | echo "::set-output name=has-backend::true" 55 | fi 56 | 57 | - name: Setup Go environment 58 | if: steps.check-for-backend.outputs.has-backend == 'true' 59 | uses: actions/setup-go@v2 60 | with: 61 | go-version: "1.16" 62 | 63 | - name: Test backend 64 | if: steps.check-for-backend.outputs.has-backend == 'true' 65 | uses: magefile/mage-action@v1 66 | with: 67 | version: latest 68 | args: coverage 69 | 70 | - name: Build backend 71 | if: steps.check-for-backend.outputs.has-backend == 'true' 72 | uses: magefile/mage-action@v1 73 | with: 74 | version: latest 75 | args: buildAll 76 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*.*.*" # Run workflow on version tags, e.g. v1.0.0. 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - name: Setup Node.js environment 16 | uses: actions/setup-node@v2.1.2 17 | with: 18 | node-version: "14.x" 19 | 20 | - name: Setup Go environment 21 | uses: actions/setup-go@v2 22 | with: 23 | go-version: "1.16" 24 | 25 | - name: Get yarn cache directory path 26 | id: yarn-cache-dir-path 27 | run: echo "::set-output name=dir::$(yarn cache dir)" 28 | 29 | - name: Cache yarn cache 30 | uses: actions/cache@v2 31 | id: cache-yarn-cache 32 | with: 33 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 34 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 35 | restore-keys: | 36 | ${{ runner.os }}-yarn- 37 | 38 | - name: Cache node_modules 39 | id: cache-node-modules 40 | uses: actions/cache@v2 41 | with: 42 | path: node_modules 43 | key: ${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/yarn.lock') }} 44 | restore-keys: | 45 | ${{ runner.os }}-${{ matrix.node-version }}-nodemodules- 46 | 47 | - name: Install dependencies 48 | run: yarn install --frozen-lockfile; 49 | if: | 50 | steps.cache-yarn-cache.outputs.cache-hit != 'true' || 51 | steps.cache-node-modules.outputs.cache-hit != 'true' 52 | 53 | - name: Build and test frontend 54 | run: yarn build 55 | 56 | - name: Check for backend 57 | id: check-for-backend 58 | run: | 59 | if [ -f "Magefile.go" ] 60 | then 61 | echo "::set-output name=has-backend::true" 62 | fi 63 | 64 | - name: Test backend 65 | if: steps.check-for-backend.outputs.has-backend == 'true' 66 | uses: magefile/mage-action@v1 67 | with: 68 | version: latest 69 | args: coverage 70 | 71 | - name: Build backend 72 | if: steps.check-for-backend.outputs.has-backend == 'true' 73 | uses: magefile/mage-action@v1 74 | with: 75 | version: latest 76 | args: buildAll 77 | 78 | # - name: Sign plugin 79 | # run: yarn sign 80 | # env: 81 | # GRAFANA_API_KEY: ${{ secrets.GRAFANA_API_KEY }} # Requires a Grafana API key from Grafana.com. 82 | 83 | # - name: Get plugin metadata 84 | # id: metadata 85 | # run: | 86 | # sudo apt-get install jq 87 | 88 | # export GRAFANA_PLUGIN_ID=$(cat dist/plugin.json | jq -r .id) 89 | # export GRAFANA_PLUGIN_VERSION=$(cat dist/plugin.json | jq -r .info.version) 90 | # export GRAFANA_PLUGIN_TYPE=$(cat dist/plugin.json | jq -r .type) 91 | # export GRAFANA_PLUGIN_ARTIFACT=${GRAFANA_PLUGIN_ID}.zip 92 | # export GRAFANA_PLUGIN_ARTIFACT_CHECKSUM=${GRAFANA_PLUGIN_ARTIFACT}.md5 93 | 94 | # echo "::set-output name=plugin-id::${GRAFANA_PLUGIN_ID}" 95 | # echo "::set-output name=plugin-version::${GRAFANA_PLUGIN_VERSION}" 96 | # echo "::set-output name=plugin-type::${GRAFANA_PLUGIN_TYPE}" 97 | # echo "::set-output name=archive::${GRAFANA_PLUGIN_ARTIFACT}" 98 | # echo "::set-output name=archive-checksum::${GRAFANA_PLUGIN_ARTIFACT_CHECKSUM}" 99 | 100 | # echo ::set-output name=github-tag::${GITHUB_REF#refs/*/} 101 | 102 | # - name: Read changelog 103 | # id: changelog 104 | # run: | 105 | # awk '/^## / {s++} s == 1 {print}' CHANGELOG.md > release_notes.md 106 | # echo "::set-output name=path::release_notes.md" 107 | 108 | # - name: Check package version 109 | # run: if [ "v${{ steps.metadata.outputs.plugin-version }}" != "${{ steps.metadata.outputs.github-tag }}" ]; then printf "\033[0;31mPlugin version doesn't match tag name\033[0m\n"; exit 1; fi 110 | 111 | # - name: Package plugin 112 | # id: package-plugin 113 | # run: | 114 | # mv dist ${{ steps.metadata.outputs.plugin-id }} 115 | # zip ${{ steps.metadata.outputs.archive }} ${{ steps.metadata.outputs.plugin-id }} -r 116 | # md5sum ${{ steps.metadata.outputs.archive }} > ${{ steps.metadata.outputs.archive-checksum }} 117 | # echo "::set-output name=checksum::$(cat ./${{ steps.metadata.outputs.archive-checksum }} | cut -d' ' -f1)" 118 | 119 | # - name: Lint plugin 120 | # run: | 121 | # git clone https://github.com/grafana/plugin-validator 122 | # pushd ./plugin-validator/cmd/plugincheck 123 | # go install 124 | # popd 125 | # plugincheck ${{ steps.metadata.outputs.archive }} 126 | 127 | # - name: Create release 128 | # id: create_release 129 | # uses: actions/create-release@v1 130 | # env: 131 | # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 132 | # with: 133 | # tag_name: ${{ github.ref }} 134 | # release_name: Release ${{ github.ref }} 135 | # body_path: ${{ steps.changelog.outputs.path }} 136 | # draft: true 137 | 138 | # - name: Add plugin to release 139 | # id: upload-plugin-asset 140 | # uses: actions/upload-release-asset@v1 141 | # env: 142 | # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 143 | # with: 144 | # upload_url: ${{ steps.create_release.outputs.upload_url }} 145 | # asset_path: ./${{ steps.metadata.outputs.archive }} 146 | # asset_name: ${{ steps.metadata.outputs.archive }} 147 | # asset_content_type: application/zip 148 | 149 | # - name: Add checksum to release 150 | # id: upload-checksum-asset 151 | # uses: actions/upload-release-asset@v1 152 | # env: 153 | # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 154 | # with: 155 | # upload_url: ${{ steps.create_release.outputs.upload_url }} 156 | # asset_path: ./${{ steps.metadata.outputs.archive-checksum }} 157 | # asset_name: ${{ steps.metadata.outputs.archive-checksum }} 158 | # asset_content_type: text/plain 159 | 160 | # - name: Publish to Grafana.com 161 | # run: | 162 | # echo A draft release has been created for your plugin. Please review and publish it. Then submit your plugin to grafana.com/plugins by opening a PR to https://github.com/grafana/grafana-plugin-repository with the following entry: 163 | # echo 164 | # echo '{ "id": "${{ steps.metadata.outputs.plugin-id }}", "type": "${{ steps.metadata.outputs.plugin-type }}", "url": "https://github.com/${{ github.repository }}", "versions": [ { "version": "${{ steps.metadata.outputs.plugin-version }}", "commit": "${{ github.sha }}", "url": "https://github.com/${{ github.repository }}", "download": { "any": { "url": "https://github.com/${{ github.repository }}/releases/download/v${{ steps.metadata.outputs.plugin-version }}/${{ steps.metadata.outputs.archive }}", "md5": "${{ steps.package-plugin.outputs.checksum }}" } } } ] }' | jq . 165 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | node_modules/ 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # Compiled binary addons (https://nodejs.org/api/addons.html) 23 | dist/ 24 | artifacts/ 25 | work/ 26 | ci/ 27 | e2e-results/ 28 | valiton-mongodbatlas-datasource/ 29 | 30 | # Editor 31 | .idea 32 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ...require("./node_modules/@grafana/toolkit/src/config/prettier.plugin.config.json"), 3 | }; -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | - **1.0.0** - Initial release 4 | 5 | Support for process, database and disk logs 6 | 7 | - **1.0.1** - Remove empty data points from atlas logs 8 | 9 | The logs by Atlas contain a lot of datapoints with null values. They were removed with this release. 10 | 11 | - **1.0.2** - Rename Email / API Token to Public Key / Private Key 12 | 13 | API keys aren't bound to accounts anymore: MongoDB deprecated the Personal API Keys in favor of the Programmatic API Keys. 14 | 15 | - **1.0.3** - Support Other Timezones 16 | 17 | https://github.com/valiton/grafana-mongodb-atlas-datasource/commit/8efac61b1d1eb7915373028e2f98986c2c42923a 18 | 19 | - **1.0.4** - Fix alerting errors 20 | 21 | https://github.com/valiton/grafana-mongodb-atlas-datasource/pull/15 22 | 23 | - **1.1.0** - Fix alerting errors 24 | https://github.com/valiton/grafana-mongodb-atlas-datasource/commit/8efac61b1d1eb7915373028e2f98986c2c42923a 25 | 26 | - **2.0.0** Add Metric & Improve Documentation 27 | - Renamed plugin from `grafana-mongodb-atlas-datasource` to `mongodb-atlas-datasource`: Required to sign the plugin 28 | - Add LOGICAL_SIZE Metric [#32](https://github.com/valiton/grafana-mongodb-atlas-datasource/issues/32) 29 | - Add security fixes 30 | - Update authentication images in README 31 | 32 | - **3.0.0** Upgraded to Grafana Plugin API v2 33 | - Renamed plugin from `mongodb-atlas-datasource` to `valiton-mongodb-atlas-datasource`: Required for new API 34 | - Rewrote complete plugin 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Valiton GmbH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Magefile.go: -------------------------------------------------------------------------------- 1 | //+build mage 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | // mage:import 8 | build "github.com/grafana/grafana-plugin-sdk-go/build" 9 | ) 10 | 11 | // Hello prints a message (shows that you can define custom Mage targets). 12 | func Hello() { 13 | fmt.Println("hello plugin developer!") 14 | } 15 | 16 | // Default configures the default target. 17 | var Default = build.BuildAll 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Grafana MongoDB Atlas Logs Datasource 2 | 3 | MongoDB Atlas allows to fetch logs from their service. More information can be found here: https://docs.atlas.mongodb.com/reference/api/logs/ 4 | 5 | This plugin allows to fetch [process](https://docs.atlas.mongodb.com/reference/api/process-measurements/), [database](https://docs.atlas.mongodb.com/reference/api/process-databases-measurements/) and [disk](https://docs.atlas.mongodb.com/reference/api/process-disks-measurements/) logs from MongoDB Atlas in your Grafana dashboard. This allows you to monitor your whole MongoDB Atlas infrastructure within your grafana dashboards. 6 | 7 | ![Panel Example](https://raw.githubusercontent.com/valiton/grafana-mongodb-atlas-datasource/master/src/img/screenshots/query_example.png) 8 | 9 | ## Installation 10 | 11 | ### Grafana Setup 12 | 13 | You can load the latest plugin version with the following command: 14 | 15 | ```bash 16 | grafana-cli --pluginUrl https://github.com/valiton/grafana-mongodb-atlas-datasource/releases/v3.0.1/download/valiton-mongodbatlas-datasource.zip plugins install valiton-mongodbatlas-datasource 17 | ``` 18 | 19 | For docker setup add the following environment variable to automatically install the plugin: 20 | 21 | ```bash 22 | docker run -p 3000:3000 \ 23 | -e GF_INSTALL_PLUGINS="https://github.com/valiton/grafana-mongodb-atlas-datasource/releases/download/v3.0.1/valiton-mongodbatlas-datasource.zip;valiton-mongodbatlas-datasource" \ 24 | -e "GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS=valiton-mongodbatlas-datasource" \ 25 | grafana/grafana:8.0.0 26 | ``` 27 | 28 | > **Note:** Plugin ID was changed from `mongodb-atlas-datasource` to `valiton-mongodbatlas-datasource` from v3.0.0 on due to the new plugin naming convention! 29 | 30 | For more information about the plugin installation have a look at the [plugin official documentation](https://grafana.com/docs/plugins/installation/). 31 | 32 | # Usage 33 | 34 | ## Create datasource 35 | 36 | After installing the datasource in Grafana (see Grafana Setup section), you can create a Grafana datasource. 37 | 38 | ![Select MongoDB Atlas Logs datasource from list](https://raw.githubusercontent.com/valiton/grafana-mongodb-atlas-datasource/master/src/img/screenshots/datasource_list.png) 39 | 40 | Please enter here your programmatic API key credentials in the two input fields and click on enter. If the credentials are valid, you will see a green info box. For more information, have a look at the [MongoDB Atlas documentation](https://docs.atlas.mongodb.com/configure-api-access/#programmatic-api-keys) to create these credentials. 41 | 42 | ![Enter your MongoDB Atlas credentials to the form](https://raw.githubusercontent.com/valiton/grafana-mongodb-atlas-datasource/master/src/img/screenshots/datasource_setup.png) 43 | 44 | ## Create Panel 45 | 46 | After setting up the datasource, you are able to create a query for a Grafana panel. You have to first select here the project you want to monitor and the cluster. After that, you can select one of three different metrics: 47 | 48 | 1. [Process logs](https://docs.atlas.mongodb.com/reference/api/process-measurements/), 49 | 2. [Database logs](https://docs.atlas.mongodb.com/reference/api/process-databases-measurements/) and 50 | 3. [Disk logs](https://docs.atlas.mongodb.com/reference/api/process-disks-measurements/) 51 | 52 | Next, you are asked different other parameters, such as the database name and then you can select the dimension you want to display in the query. To name the query, please use the `alias` input. You can use `{{name}}` to use metrics or dimensions for the name (see hint field of `alias` for more information). 53 | 54 | ![Enter parameters for your MongoDB Atlas Query](https://raw.githubusercontent.com/valiton/grafana-mongodb-atlas-datasource/master/src/img/screenshots/query_setup.png) 55 | 56 | # Dev setup 57 | 58 | ## Frontend 59 | 60 | 1. Install dependencies 61 | 62 | ```bash 63 | yarn install 64 | ``` 65 | 66 | 2. Build plugin in development mode or run in watch mode 67 | 68 | ```bash 69 | yarn dev 70 | ``` 71 | 72 | or 73 | 74 | ```bash 75 | yarn watch 76 | ``` 77 | 78 | 3. Build plugin in production mode 79 | 80 | ```bash 81 | yarn build 82 | ``` 83 | 84 | ## Backend 85 | 86 | 1. Update [Grafana plugin SDK for Go](https://grafana.com/docs/grafana/latest/developers/plugins/backend/grafana-plugin-sdk-for-go/) dependency to the latest minor version: 87 | 88 | ```bash 89 | go get -u github.com/grafana/grafana-plugin-sdk-go 90 | go mod tidy 91 | ``` 92 | 93 | 2. Build backend plugin binaries for Linux, Windows and Darwin: 94 | 95 | ```bash 96 | mage -v 97 | ``` 98 | 99 | 3. List all available Mage targets for additional commands: 100 | 101 | ```bash 102 | mage -l 103 | ``` 104 | 105 | # Limitations 106 | 107 | - Annotations are not supported yet 108 | 109 | # Contributing 110 | 111 | Pull requests for new features, bug fixes, and suggestions are welcome! 112 | 113 | # Release 114 | 115 | **1. Add Release Notes to Changelog in CHANGELOG.md** 116 | 117 | **2. Update package.json version** 118 | 119 | **3. Create Tag with format vx.y.z** 120 | > We use semversion format for tagging the releases.´ 121 | 122 | **4. Create Release Zip** 123 | 124 | ```bash 125 | yarn install 126 | npm run build 127 | mage -v 128 | rm -Rf valiton-mongodbatlas-datasource && \ 129 | cp -R dist valiton-mongodbatlas-datasource 130 | ``` 131 | 132 | Next, please remove all dev-related topics from the `valiton-mongodbatlas-datasource/README.md` file. Otherwise, we can not publish it as Grafana plugin (only keep introduction & usage section). 133 | 134 | Next, bundle it as 135 | 136 | ```bash 137 | zip -r valiton-mongodbatlas-datasource.zip ./valiton-mongodbatlas-datasource 138 | ``` 139 | 140 | **5. Create Release with zip files as attachment** 141 | 142 | see https://help.github.com/en/articles/creating-releases for more information 143 | 144 | # Changelog 145 | 146 | [Changelog](https://github.com/valiton/grafana-mongodb-atlas-datasource/blob/master/CHANGELOG.md) 147 | 148 | # License 149 | 150 | [MIT](https://github.com/valiton/grafana-mongodb-atlas-datasource/blob/master/LICENSE) 151 | 152 | # Thanks to 153 | 154 | We also want to thank the Grafana team for their [Github Datasource](https://github.com/grafana/github-datasource) that helped us to get started and we also used some of their code parts. This decreased our development effort a lot, which made it easier for us to switch to the new Grafana Plugin v2 version! :-) 155 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/valiton/grafana-mongodb-atlas-datasource 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/bitly/go-simplejson v0.5.0 7 | github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect 8 | github.com/gorilla/mux v1.7.3 9 | github.com/grafana/grafana-plugin-sdk-go v0.105.0 10 | github.com/pkg/errors v0.9.1 11 | github.com/xinsnake/go-http-digest-auth-client v0.6.0 12 | ) 13 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 4 | github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= 5 | github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= 6 | github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= 7 | github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= 8 | github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= 9 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 10 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 11 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 12 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 13 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= 14 | github.com/apache/arrow/go/arrow v0.0.0-20210223225224-5bea62493d91 h1:rbe942bXzd2vnds4y9fYQL8X4yFltXoZsKW7KtG+TFM= 15 | github.com/apache/arrow/go/arrow v0.0.0-20210223225224-5bea62493d91/go.mod h1:c9sxoIT3YgLxH4UhLOCKaBlEojuMhVYpk4Ntv3opUTQ= 16 | github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= 17 | github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= 18 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 19 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 20 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 21 | github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= 22 | github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= 23 | github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= 24 | github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= 25 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 26 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 27 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 28 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 29 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 30 | github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= 31 | github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= 32 | github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= 33 | github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= 34 | github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= 35 | github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= 36 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 37 | github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= 38 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 39 | github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= 40 | github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= 41 | github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= 42 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 43 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 44 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 45 | github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= 46 | github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= 47 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 48 | github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 49 | github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 50 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 51 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 52 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 53 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 54 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 55 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 56 | github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 57 | github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= 58 | github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= 59 | github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= 60 | github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= 61 | github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= 62 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 63 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 64 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 65 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 66 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 67 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 68 | github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= 69 | github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= 70 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 71 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 72 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 73 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 74 | github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= 75 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 76 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 77 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 78 | github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 79 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 80 | github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= 81 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 82 | github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 83 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 84 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 85 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 86 | github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 87 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 88 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 89 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 90 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 91 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 92 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 93 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 94 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 95 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 96 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 97 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 98 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 99 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 100 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 101 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 102 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 103 | github.com/golang/protobuf v1.5.1 h1:jAbXjIeW2ZSW2AwFxlGTDoc2CjI2XujLkV3ArsZFCvc= 104 | github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= 105 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 106 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 107 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 108 | github.com/google/flatbuffers v1.11.0 h1:O7CEyB8Cb3/DmtxODGtLHcEvpr81Jm5qLg/hsHnxA2A= 109 | github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= 110 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 111 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 112 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 113 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 114 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 115 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 116 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 117 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 118 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 119 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 120 | github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 121 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 122 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 123 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= 124 | github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 125 | github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= 126 | github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 127 | github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 128 | github.com/grafana/grafana-plugin-sdk-go v0.105.0 h1:I0r88FtnXkWw4F0t36cmRCupizY4cPkK+6PKKqbyx9Q= 129 | github.com/grafana/grafana-plugin-sdk-go v0.105.0/go.mod h1:D7x3ah+1d4phNXpbnOaxa/osSaZlwh9/ZUnGGzegRbk= 130 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 131 | github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= 132 | github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= 133 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= 134 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 135 | github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 136 | github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= 137 | github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 138 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 139 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 140 | github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd h1:rNuUHR+CvK1IS89MMtcF0EpcVMZtjKfPRp4MEmt/aTs= 141 | github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= 142 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 143 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 144 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 145 | github.com/hashicorp/go-plugin v1.2.2 h1:mgDpq0PkoK5gck2w4ivaMpWRHv/matdOR4xmeScmf/w= 146 | github.com/hashicorp/go-plugin v1.2.2/go.mod h1:F9eH4LrE/ZsRdbwhfjs9k9HoDUwAHnYtXdgmf1AVNs0= 147 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 148 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 149 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 150 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 151 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 152 | github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 153 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 154 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 155 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 156 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 157 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 158 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 159 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 160 | github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= 161 | github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= 162 | github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= 163 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 164 | github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= 165 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 166 | github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= 167 | github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= 168 | github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= 169 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 170 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 171 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 172 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 173 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 174 | github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 175 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 176 | github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= 177 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 178 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 179 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 180 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 181 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 182 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 183 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 184 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 185 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 186 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 187 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 188 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 189 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 190 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 191 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 192 | github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= 193 | github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= 194 | github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= 195 | github.com/magefile/mage v1.11.0 h1:C/55Ywp9BpgVVclD3lRnSYCwXTYxmSppIgLeDYlNuls= 196 | github.com/magefile/mage v1.11.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= 197 | github.com/mattetti/filebuffer v1.0.1 h1:gG7pyfnSIZCxdoKq+cPa8T0hhYtD9NxCdI4D7PTjRLM= 198 | github.com/mattetti/filebuffer v1.0.1/go.mod h1:YdMURNDOttIiruleeVr6f56OrMc+MydEnTcXwtkxNVs= 199 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 200 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 201 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 202 | github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 203 | github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= 204 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 205 | github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= 206 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 207 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 208 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 209 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 210 | github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 211 | github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= 212 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 213 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 214 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 215 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 216 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 217 | github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= 218 | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 219 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 220 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 221 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 222 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 223 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 224 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 225 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 226 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 227 | github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= 228 | github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= 229 | github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= 230 | github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= 231 | github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= 232 | github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= 233 | github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 234 | github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= 235 | github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= 236 | github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= 237 | github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 238 | github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 239 | github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= 240 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 241 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 242 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 243 | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= 244 | github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= 245 | github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= 246 | github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 247 | github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 248 | github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= 249 | github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= 250 | github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= 251 | github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= 252 | github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= 253 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 254 | github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= 255 | github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= 256 | github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= 257 | github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 258 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 259 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 260 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 261 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 262 | github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= 263 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 264 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 265 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 266 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 267 | github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= 268 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 269 | github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= 270 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 271 | github.com/prometheus/client_golang v1.10.0 h1:/o0BDeWzLWXNZ+4q5gXltUvaMpJqckTa+jTNoB+z4cg= 272 | github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= 273 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 274 | github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 275 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 276 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 277 | github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 278 | github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= 279 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 280 | github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 281 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 282 | github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= 283 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= 284 | github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= 285 | github.com/prometheus/common v0.23.0 h1:GXWvPYuTUenIa+BhOq/x+L/QZzCqASkVRny5KTlPDGM= 286 | github.com/prometheus/common v0.23.0/go.mod h1:H6QK/N6XVT42whUeIdI3dp36w49c+/iMDk7UAI2qm7Q= 287 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 288 | github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 289 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 290 | github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 291 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 292 | github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= 293 | github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 294 | github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= 295 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 296 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 297 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 298 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 299 | github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= 300 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 301 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 302 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 303 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 304 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 305 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 306 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 307 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 308 | github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= 309 | github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 310 | github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 311 | github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= 312 | github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= 313 | github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= 314 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 315 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 316 | github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 317 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 318 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 319 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 320 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 321 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 322 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 323 | github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 324 | github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= 325 | github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 326 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 327 | github.com/xinsnake/go-http-digest-auth-client v0.6.0 h1:nrYFWDrB2F7VwYlNravXZS0nOtg9axlATH3Jns55/F0= 328 | github.com/xinsnake/go-http-digest-auth-client v0.6.0/go.mod h1:QK1t1v7ylyGb363vGWu+6Irh7gyFj+N7+UZzM0L6g8I= 329 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 330 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 331 | go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 332 | go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= 333 | go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= 334 | go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= 335 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 336 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 337 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 338 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 339 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 340 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 341 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 342 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 343 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 344 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 345 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 346 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 347 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 348 | golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 349 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 350 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 351 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 352 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 353 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 354 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 355 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 356 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 357 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 358 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 359 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 360 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 361 | golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 362 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 363 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 364 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 365 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 366 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 367 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 368 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 369 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 370 | golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 371 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 372 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 373 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 374 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 375 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 376 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 377 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 378 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 379 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 380 | golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 381 | golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= 382 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 383 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 384 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 385 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 386 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 387 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 388 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 389 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 390 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 391 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 392 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 393 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 394 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 395 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 396 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 397 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 398 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 399 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 400 | golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 401 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 402 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 403 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 404 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 405 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 406 | golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 407 | golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 408 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 409 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 410 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 411 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 412 | golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 413 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 414 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 415 | golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY= 416 | golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 417 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 418 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 419 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 420 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 421 | golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 422 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 423 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 424 | golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 425 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 426 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 427 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 428 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 429 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 430 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 431 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 432 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 433 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 434 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 435 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 436 | golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 437 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 438 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 439 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 440 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 441 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 442 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 443 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 444 | google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= 445 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 446 | google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 447 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 448 | google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 449 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 450 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 451 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 452 | google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= 453 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 454 | google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 455 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 456 | google.golang.org/genproto v0.0.0-20200911024640-645f7a48b24f h1:Yv4xsIx7HZOoyUGSJ2ksDyWE2qIBXROsZKt2ny3hCGM= 457 | google.golang.org/genproto v0.0.0-20200911024640-645f7a48b24f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 458 | google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= 459 | google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= 460 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 461 | google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= 462 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 463 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 464 | google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 465 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 466 | google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 467 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 468 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 469 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 470 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 471 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 472 | google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 473 | google.golang.org/grpc v1.37.1 h1:ARnQJNWxGyYJpdf/JXscNlQr/uv607ZPU9Z7ogHi+iI= 474 | google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 475 | google.golang.org/grpc/cmd/protoc-gen-go-grpc v0.0.0-20200910201057-6591123024b3/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 476 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 477 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 478 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 479 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 480 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 481 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 482 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 483 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 484 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 485 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 486 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 487 | google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= 488 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 489 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 490 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 491 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 492 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 493 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 494 | gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= 495 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 496 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 497 | gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= 498 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 499 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 500 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 501 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 502 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 503 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 504 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 505 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 506 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 507 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 508 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= 509 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 510 | honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 511 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 512 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 513 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 514 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 515 | sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= 516 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // This file is needed because it is used by vscode and other tools that 2 | // call `jest` directly. However, unless you are doing anything special 3 | // do not edit this file 4 | 5 | const standard = require('@grafana/toolkit/src/config/jest.plugin.config'); 6 | 7 | // This process will use the same config that `yarn test` is using 8 | module.exports = standard.jestConfig(); 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "valiton-valiton-mongodbatlas-datasource", 3 | "version": "3.0.1", 4 | "description": "Display MongoDB Atlas metrics inside of Grafana", 5 | "scripts": { 6 | "build": "grafana-toolkit plugin:build", 7 | "test": "grafana-toolkit plugin:test", 8 | "dev": "grafana-toolkit plugin:dev", 9 | "watch": "grafana-toolkit plugin:dev --watch", 10 | "sign": "grafana-toolkit plugin:sign", 11 | "start": "yarn watch" 12 | }, 13 | "author": "Christoph Caprano", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "@grafana/data": "latest", 17 | "@grafana/runtime": "latest", 18 | "@grafana/toolkit": "latest", 19 | "@grafana/ui": "latest", 20 | "@types/lodash": "latest" 21 | }, 22 | "resolutions": { 23 | "rxjs": "6.6.3" 24 | }, 25 | "engines": { 26 | "node": ">=14" 27 | }, 28 | "dependencies": { 29 | "humanize-string": "^2.1.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /pkg/datasource/client.go: -------------------------------------------------------------------------------- 1 | package datasource 2 | 3 | import ( 4 | "net/http" 5 | "context" 6 | "fmt" 7 | "time" 8 | "io/ioutil" 9 | dac "github.com/xinsnake/go-http-digest-auth-client" 10 | 11 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/models" 12 | 13 | "github.com/grafana/grafana-plugin-sdk-go/backend/log" 14 | ) 15 | 16 | type MongoDBAtlasClient struct { 17 | settings *models.Settings 18 | } 19 | 20 | func (c *MongoDBAtlasClient) query(ctx context.Context, path string, query map[string]string) ([]byte, error) { 21 | apiType := c.settings.ApiType 22 | if apiType != "atlas" && apiType != "public" { 23 | apiType = "atlas" 24 | } 25 | 26 | var method = "GET" 27 | var baseURL = "https://cloud.mongodb.com/api/" + apiType + "/v1.0" 28 | var uri = baseURL + path 29 | 30 | log.DefaultLogger.Debug("MakeHttpRequest", "URL", uri) 31 | 32 | var t = dac.NewTransport(c.settings.PublicKey, c.settings.PrivateKey) 33 | t.HTTPClient = &http.Client{ 34 | Timeout: time.Second * 10, 35 | Transport: &http.Transport{ 36 | Proxy: http.ProxyFromEnvironment, 37 | }, 38 | } 39 | req, err := http.NewRequest(method, uri, nil) 40 | 41 | if query != nil { 42 | q := req.URL.Query() 43 | for key, value := range query { 44 | q.Add(key, value) 45 | } 46 | req.URL.RawQuery = q.Encode() 47 | } 48 | 49 | log.DefaultLogger.Debug("MakeHttpRequest", "Full URL", req.URL.RequestURI()) 50 | 51 | if err != nil { 52 | return nil, err 53 | } 54 | 55 | resp, err := t.RoundTrip(req) 56 | 57 | if err != nil { 58 | log.DefaultLogger.Info("MakeHttpRequest", "error", err) 59 | return nil, err 60 | } 61 | 62 | body, err := ioutil.ReadAll(resp.Body) 63 | 64 | if err != nil { 65 | log.DefaultLogger.Debug("MakeHttpRequest", "io error", err) 66 | return nil, err 67 | } 68 | 69 | if resp.StatusCode != http.StatusOK { 70 | err = fmt.Errorf("invalid status code. status: %v", resp.Status) 71 | log.DefaultLogger.Debug("MakeHttpRequest", "status code error", err) 72 | return nil, err 73 | } 74 | 75 | log.DefaultLogger.Debug("MakeHttpRequest", "body", string(body)) 76 | 77 | return body, nil 78 | } 79 | -------------------------------------------------------------------------------- /pkg/datasource/clusters.go: -------------------------------------------------------------------------------- 1 | package datasource 2 | 3 | import ( 4 | "context" 5 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/models" 6 | 7 | simplejson "github.com/bitly/go-simplejson" 8 | ) 9 | 10 | type Cluster struct { 11 | ID string `json:"id"` 12 | Name string `json:"name"` 13 | } 14 | 15 | type Clusters []Cluster 16 | 17 | func GetClusters(ctx context.Context, client *MongoDBAtlasClient, opts models.ListClustersOptions) (Clusters, error) { 18 | body, err := client.query(ctx, "/groups/"+opts.Project+"/clusters", nil) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | jBody, err := simplejson.NewJson(body) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | var unformattedClusters = jBody.Get("results") 29 | var numClusters = len(unformattedClusters.MustArray()) 30 | var clusters = make(Clusters, numClusters) 31 | for i := 0; i < numClusters; i++ { 32 | var jCluster = unformattedClusters.GetIndex(i) 33 | 34 | clusters[i] = Cluster{ 35 | ID: jCluster.Get("id").MustString(), 36 | Name: jCluster.Get("name").MustString(), 37 | } 38 | } 39 | 40 | return clusters, nil 41 | } -------------------------------------------------------------------------------- /pkg/datasource/databases.go: -------------------------------------------------------------------------------- 1 | package datasource 2 | 3 | import ( 4 | "context" 5 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/models" 6 | 7 | simplejson "github.com/bitly/go-simplejson" 8 | ) 9 | 10 | type Databases []string 11 | 12 | func GetDatabases(ctx context.Context, client *MongoDBAtlasClient, opts models.ListDatabasesOptions) (Databases, error) { 13 | body, err := client.query(ctx, "/groups/"+opts.Project+"/processes/"+opts.Mongo+"/databases", nil) 14 | if err != nil { 15 | return nil, err 16 | } 17 | 18 | jBody, err := simplejson.NewJson(body) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | var unformattedDatabases = jBody.Get("results") 24 | var numDatabases = len(unformattedDatabases.MustArray()) 25 | var databases = make(Databases, numDatabases) 26 | for i := 0; i < numDatabases; i++ { 27 | var jDatabase = unformattedDatabases.GetIndex(i) 28 | databases[i] = jDatabase.Get("databaseName").MustString() 29 | } 30 | 31 | return databases, nil 32 | } -------------------------------------------------------------------------------- /pkg/datasource/datasource.go: -------------------------------------------------------------------------------- 1 | package datasource 2 | 3 | import ( 4 | "context" 5 | "time" 6 | "strings" 7 | 8 | simplejson "github.com/bitly/go-simplejson" 9 | 10 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/dfutil" 11 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/models" 12 | "github.com/grafana/grafana-plugin-sdk-go/backend/log" 13 | "github.com/grafana/grafana-plugin-sdk-go/backend" 14 | "github.com/grafana/grafana-plugin-sdk-go/data" 15 | ) 16 | 17 | 18 | 19 | type Datasource struct { 20 | client *MongoDBAtlasClient 21 | } 22 | 23 | func (d *Datasource) CheckHealth(ctx context.Context) error { 24 | _, err := d.client.query(ctx, "/", nil) 25 | 26 | return err 27 | } 28 | 29 | func unixToRFC3339(date int64) string { 30 | return time.Unix(date, 0).UTC().Format(time.RFC3339) 31 | } 32 | 33 | func formatInterval(interval time.Duration) int64 { 34 | return int64(interval / time.Millisecond) 35 | } 36 | 37 | type MeasurementOptions struct { 38 | Start string 39 | End string 40 | IntervalMs int64 41 | Measurement string 42 | } 43 | 44 | type DataPoint struct { 45 | Timestamp string 46 | Value float64 47 | } 48 | 49 | type DataFrame struct { 50 | Name string 51 | Points []DataPoint 52 | } 53 | 54 | func (df DataFrame) Frames() data.Frames { 55 | frame := data.NewFrame( 56 | "logs", 57 | data.NewField("time", nil, []time.Time{}), 58 | data.NewField(df.Name, nil, []float64{}), 59 | ) 60 | 61 | for _, v := range df.Points { 62 | timestamp, _ := time.Parse(time.RFC3339, v.Timestamp) 63 | frame.AppendRow( 64 | timestamp, 65 | v.Value, 66 | ) 67 | } 68 | 69 | return data.Frames{frame} 70 | } 71 | 72 | 73 | func GetMeasurementOptions(options *MeasurementOptions) map[string]string { 74 | var granularity string 75 | 76 | if options.IntervalMs <= 60000 { 77 | granularity = "PT1M" 78 | } else if options.IntervalMs <= 500000 { 79 | granularity = "PT5M" 80 | } else if options.IntervalMs <= 3600000 { 81 | granularity = "PT1H" 82 | } else { 83 | granularity = "PT1D" 84 | } 85 | 86 | return map[string]string{ 87 | "start": options.Start, 88 | "end": options.End, 89 | "m": options.Measurement, 90 | "granularity": granularity, 91 | } 92 | } 93 | 94 | func GetMeasurements(name string, body []byte, ctx context.Context) (*DataFrame, error) { 95 | jBody, err := simplejson.NewJson(body) 96 | if err != nil { 97 | return nil, err 98 | } 99 | 100 | df := &DataFrame{ 101 | Name: name, 102 | Points: make([]DataPoint, 0), 103 | } 104 | 105 | jMeasurements := jBody.Get("measurements") 106 | log.DefaultLogger.Debug("GetMeasurements", "measurements", jMeasurements) 107 | if len(jMeasurements.MustArray()) == 0 { 108 | return df, nil 109 | } 110 | firstMeasurement := jMeasurements.GetIndex(0) 111 | log.DefaultLogger.Debug("GetMeasurements", "first measurement", firstMeasurement) 112 | var rawDataPoints = firstMeasurement.Get("dataPoints") 113 | log.DefaultLogger.Debug("GetMeasurements", "raw data points", rawDataPoints) 114 | var numDataPoints = len(rawDataPoints.MustArray()) 115 | var dataPoints = make([]DataPoint, 0, numDataPoints) 116 | for i := 0; i < numDataPoints; i++ { 117 | var jDataPoint = rawDataPoints.GetIndex(i) 118 | 119 | dataPointValue := jDataPoint.Get("value") 120 | 121 | // filter out all JSON null values that are sent by Atlas 122 | if dataPointValue.Interface() == nil { 123 | continue 124 | } 125 | 126 | log.DefaultLogger.Debug("GetMeasurements", "data point", jDataPoint) 127 | 128 | dataPoint := DataPoint{ 129 | Timestamp: jDataPoint.Get("timestamp").MustString(), 130 | Value: dataPointValue.MustFloat64(), 131 | } 132 | dataPoints = append(dataPoints, dataPoint) 133 | } 134 | 135 | df.Points = dataPoints; 136 | 137 | log.DefaultLogger.Debug("GetMeasurements", "Final data points", dataPoints) 138 | 139 | return df, nil 140 | } 141 | 142 | func getName(refId, alias, projectID, projectName, clusterID, clusterName, database, mongo, disk, dimensionName string) string { 143 | name := refId 144 | if alias != "" { 145 | name = alias 146 | name = strings.ReplaceAll(name, "{{projectId}}", projectID) 147 | name = strings.ReplaceAll(name, "{{projectName}}", projectName) 148 | name = strings.ReplaceAll(name, "{{clusterId}}", clusterID) 149 | name = strings.ReplaceAll(name, "{{clusterName}}", clusterName) 150 | name = strings.ReplaceAll(name, "{{database}}", database) 151 | name = strings.ReplaceAll(name, "{{mongo}}", mongo) 152 | name = strings.ReplaceAll(name, "{{disk}}", disk) 153 | name = strings.ReplaceAll(name, "{{dimensionName}}", dimensionName) 154 | } 155 | return name 156 | } 157 | 158 | func (d *Datasource) HandleProcessMeasurementsQuery(ctx context.Context, query *models.ProcessMeasurementsQuery, req backend.DataQuery) (dfutil.Framer, error) { 159 | project := query.Project.Value 160 | mongo := query.Mongo.Value 161 | options := &MeasurementOptions{ 162 | Start: unixToRFC3339(req.TimeRange.From.Unix()), 163 | End: unixToRFC3339(req.TimeRange.To.Unix()), 164 | IntervalMs: formatInterval(req.Interval), 165 | Measurement: query.Dimension.Value, 166 | } 167 | 168 | name := getName(query.RefId, query.Alias, query.Project.Value, query.Project.Label, query.Cluster.Value, query.Cluster.Label, "", mongo, "", query.Dimension.Value) 169 | 170 | body, err := d.client.query(ctx, "/groups/"+project+"/processes/"+mongo+"/measurements", GetMeasurementOptions(options)) 171 | if err != nil { 172 | return nil, err 173 | } 174 | return GetMeasurements(name, body, ctx) 175 | } 176 | 177 | func (d *Datasource) HandleDatabaseMeasurementsQuery(ctx context.Context, query *models.DatabaseMeasurementsQuery, req backend.DataQuery) (dfutil.Framer, error) { 178 | project := query.Project.Value 179 | mongo := query.Mongo.Value 180 | database := query.Database.Value 181 | options := &MeasurementOptions{ 182 | Start: unixToRFC3339(req.TimeRange.From.Unix()), 183 | End: unixToRFC3339(req.TimeRange.To.Unix()), 184 | IntervalMs: formatInterval(req.Interval), 185 | Measurement: query.Dimension.Value, 186 | } 187 | 188 | name := getName(query.RefId, query.Alias, query.Project.Value, query.Project.Label, query.Cluster.Value, query.Cluster.Label, database, mongo, "", query.Dimension.Value) 189 | 190 | body, err := d.client.query(ctx, "/groups/"+project+"/processes/"+mongo+"/databases/"+database+"/measurements", GetMeasurementOptions(options)) 191 | if err != nil { 192 | return nil, err 193 | } 194 | return GetMeasurements(name, body, ctx) 195 | } 196 | 197 | func (d *Datasource) HandleDiskMeasurementsQuery(ctx context.Context, query *models.DiskMeasurementsQuery, req backend.DataQuery) (dfutil.Framer, error) { 198 | project := query.Project.Value 199 | mongo := query.Mongo.Value 200 | disk := query.Disk.Value 201 | options := &MeasurementOptions{ 202 | Start: unixToRFC3339(req.TimeRange.From.Unix()), 203 | End: unixToRFC3339(req.TimeRange.To.Unix()), 204 | IntervalMs: formatInterval(req.Interval), 205 | Measurement: query.Dimension.Value, 206 | } 207 | 208 | name := getName(query.RefId, query.Alias, query.Project.Value, query.Project.Label, query.Cluster.Value, query.Cluster.Label, "", mongo, disk, query.Dimension.Value) 209 | 210 | body, err := d.client.query(ctx, "/groups/"+project+"/processes/"+mongo+"/disks/"+disk+"/measurements", GetMeasurementOptions(options)) 211 | if err != nil { 212 | return nil, err 213 | } 214 | return GetMeasurements(name, body, ctx) 215 | } 216 | 217 | // NewDatasource creates a new datasource for handling queries 218 | func NewDatasource(ctx context.Context, settings *models.Settings) *Datasource { 219 | client := &MongoDBAtlasClient{ 220 | settings: settings, 221 | } 222 | return &Datasource{ 223 | client: client, 224 | } 225 | } -------------------------------------------------------------------------------- /pkg/datasource/disks.go: -------------------------------------------------------------------------------- 1 | package datasource 2 | 3 | import ( 4 | "context" 5 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/models" 6 | 7 | simplejson "github.com/bitly/go-simplejson" 8 | ) 9 | 10 | type DiskName string 11 | 12 | type Disks []DiskName 13 | 14 | func GetDisks(ctx context.Context, client *MongoDBAtlasClient, opts models.ListDisksOptions) (Disks, error) { 15 | body, err := client.query(ctx, "/groups/"+opts.Project+"/processes/"+opts.Mongo+"/disks", nil) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | jBody, err := simplejson.NewJson(body) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | var unformattedDisks = jBody.Get("results") 26 | var numDisks = len(unformattedDisks.MustArray()) 27 | var disks = make([]DiskName, numDisks) 28 | for i := 0; i < numDisks; i++ { 29 | var jDisk = unformattedDisks.GetIndex(i) 30 | disks[i] = DiskName(jDisk.Get("partitionName").MustString()) 31 | } 32 | 33 | return disks, nil 34 | } -------------------------------------------------------------------------------- /pkg/datasource/mongos.go: -------------------------------------------------------------------------------- 1 | package datasource 2 | 3 | import ( 4 | "context" 5 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/models" 6 | 7 | simplejson "github.com/bitly/go-simplejson" 8 | ) 9 | 10 | type Mongo struct { 11 | ID string `json:"id"` 12 | Name string `json:"name"` 13 | } 14 | 15 | type Mongos []Mongo 16 | 17 | func GetMongos(ctx context.Context, client *MongoDBAtlasClient, opts models.ListMongosOptions) (Mongos, error) { 18 | body, err := client.query(ctx, "/groups/"+opts.Project+"/processes", nil) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | jBody, err := simplejson.NewJson(body) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | var unformattedClusters = jBody.Get("results") 29 | var numMongos = len(unformattedClusters.MustArray()) 30 | var mongos = make([]Mongo, numMongos) 31 | for i := 0; i < numMongos; i++ { 32 | var jMongo = unformattedClusters.GetIndex(i) 33 | 34 | mongos[i] = Mongo{ 35 | ID: jMongo.Get("id").MustString(), 36 | Name: jMongo.Get("hostname").MustString(), 37 | } 38 | } 39 | 40 | return mongos, nil 41 | } -------------------------------------------------------------------------------- /pkg/datasource/projects.go: -------------------------------------------------------------------------------- 1 | package datasource 2 | 3 | import ( 4 | "context" 5 | "github.com/grafana/grafana-plugin-sdk-go/backend/log" 6 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/models" 7 | 8 | simplejson "github.com/bitly/go-simplejson" 9 | ) 10 | 11 | type Project struct { 12 | ID string `json:"id"` 13 | Name string `json:"name"` 14 | } 15 | 16 | type Projects []Project 17 | 18 | func GetProjects(ctx context.Context, client *MongoDBAtlasClient, opts models.ListProjectsOptions) (Projects, error) { 19 | body, err := client.query(ctx, "/groups", nil) 20 | if err != nil { 21 | log.DefaultLogger.Debug("GetProjects", "HTTP Error", err) 22 | return nil, err 23 | } 24 | 25 | jBody, err := simplejson.NewJson(body) 26 | if err != nil { 27 | log.DefaultLogger.Debug("GetProjects", "JSON Parse Error", err) 28 | return nil, err 29 | } 30 | 31 | var unformattedProjects = jBody.Get("results") 32 | var numProjects = len(unformattedProjects.MustArray()) 33 | 34 | log.DefaultLogger.Debug("GetProjects", "raw projects", unformattedProjects, "num projects", numProjects) 35 | 36 | var projects = make(Projects, numProjects) 37 | for i := 0; i < numProjects; i++ { 38 | var jProject = unformattedProjects.GetIndex(i) 39 | log.DefaultLogger.Debug("GetProjects", "jProject", jProject, "index", i) 40 | project := Project{ 41 | ID: jProject.Get("id").MustString(), 42 | Name: jProject.Get("name").MustString(), 43 | } 44 | projects[i] = project 45 | log.DefaultLogger.Debug("GetProjects", "project", project) 46 | } 47 | 48 | return projects, nil 49 | } -------------------------------------------------------------------------------- /pkg/datasource/resource_handlers.go: -------------------------------------------------------------------------------- 1 | package datasource 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/httputil" 8 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/models" 9 | ) 10 | 11 | func handleGetProjects(ctx context.Context, client *MongoDBAtlasClient, r *http.Request) (Projects, error) { 12 | opts := models.ListProjectsOptions{} 13 | 14 | labels, err := GetProjects(ctx, client, opts) 15 | if err != nil { 16 | return nil, err 17 | } 18 | 19 | return labels, nil 20 | } 21 | 22 | func (d *Datasource) HandleGetProjects(w http.ResponseWriter, r *http.Request) { 23 | projects, err := handleGetProjects(r.Context(), d.client, r) 24 | if err != nil { 25 | httputil.WriteError(w, http.StatusBadRequest, err) 26 | return 27 | } 28 | 29 | httputil.WriteResponse(w, projects) 30 | } 31 | 32 | func handleGetClusters(ctx context.Context, client *MongoDBAtlasClient, r *http.Request) (Clusters, error) { 33 | q := r.URL.Query() 34 | opts := models.ListClustersOptions{ 35 | Project: q.Get("project"), 36 | } 37 | 38 | labels, err := GetClusters(ctx, client, opts) 39 | if err != nil { 40 | return nil, err 41 | } 42 | 43 | return labels, nil 44 | } 45 | 46 | func (d *Datasource) HandleGetClusters(w http.ResponseWriter, r *http.Request) { 47 | clusters, err := handleGetClusters(r.Context(), d.client, r) 48 | if err != nil { 49 | httputil.WriteError(w, http.StatusBadRequest, err) 50 | return 51 | } 52 | 53 | httputil.WriteResponse(w, clusters) 54 | } 55 | 56 | func handleGetMongos(ctx context.Context, client *MongoDBAtlasClient, r *http.Request) (Mongos, error) { 57 | q := r.URL.Query() 58 | opts := models.ListMongosOptions{ 59 | Project: q.Get("project"), 60 | } 61 | 62 | mongos, err := GetMongos(ctx, client, opts) 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | return mongos, nil 68 | } 69 | 70 | func (d *Datasource) HandleGetMongos(w http.ResponseWriter, r *http.Request) { 71 | mongos, err := handleGetMongos(r.Context(), d.client, r) 72 | if err != nil { 73 | httputil.WriteError(w, http.StatusBadRequest, err) 74 | return 75 | } 76 | 77 | httputil.WriteResponse(w, mongos) 78 | } 79 | 80 | func handleGetDisks(ctx context.Context, client *MongoDBAtlasClient, r *http.Request) (Disks, error) { 81 | q := r.URL.Query() 82 | opts := models.ListDisksOptions{ 83 | Project: q.Get("project"), 84 | Mongo: q.Get("mongo"), 85 | } 86 | 87 | disks, err := GetDisks(ctx, client, opts) 88 | if err != nil { 89 | return nil, err 90 | } 91 | 92 | return disks, nil 93 | } 94 | 95 | func (d *Datasource) HandleGetDisks(w http.ResponseWriter, r *http.Request) { 96 | disks, err := handleGetDisks(r.Context(), d.client, r) 97 | if err != nil { 98 | httputil.WriteError(w, http.StatusBadRequest, err) 99 | return 100 | } 101 | 102 | httputil.WriteResponse(w, disks) 103 | } 104 | 105 | func handleGetDatabases(ctx context.Context, client *MongoDBAtlasClient, r *http.Request) (Databases, error) { 106 | q := r.URL.Query() 107 | opts := models.ListDatabasesOptions{ 108 | Project: q.Get("project"), 109 | Mongo: q.Get("mongo"), 110 | } 111 | 112 | databases, err := GetDatabases(ctx, client, opts) 113 | if err != nil { 114 | return nil, err 115 | } 116 | 117 | return databases, nil 118 | } 119 | 120 | func (d *Datasource) HandleGetDatabases(w http.ResponseWriter, r *http.Request) { 121 | databases, err := handleGetDatabases(r.Context(), d.client, r) 122 | if err != nil { 123 | httputil.WriteError(w, http.StatusBadRequest, err) 124 | return 125 | } 126 | 127 | httputil.WriteResponse(w, databases) 128 | } -------------------------------------------------------------------------------- /pkg/dfutil/framer.go: -------------------------------------------------------------------------------- 1 | package dfutil 2 | 3 | import ( 4 | "github.com/grafana/grafana-plugin-sdk-go/backend" 5 | "github.com/grafana/grafana-plugin-sdk-go/data" 6 | ) 7 | 8 | // Framer is an interface that allows any type to be treated as a data frame 9 | type Framer interface { 10 | Frames() data.Frames 11 | } 12 | 13 | // FrameResponse creates a backend.DataResponse that contains the Framer's data.Frames 14 | func FrameResponse(f Framer) backend.DataResponse { 15 | return backend.DataResponse{ 16 | Frames: f.Frames(), 17 | } 18 | } 19 | 20 | // FrameResponseWithError creates a backend.DataResponse with the error's contents (if not nil), and the Framer's data.Frames 21 | // This function is particularly useful if you have a function that returns `(Framer, error)`, which is a very common pattern 22 | func FrameResponseWithError(f Framer, err error) backend.DataResponse { 23 | if err != nil { 24 | return backend.DataResponse{ 25 | Error: err, 26 | } 27 | } 28 | 29 | return FrameResponse(f) 30 | } -------------------------------------------------------------------------------- /pkg/errors/errors.go: -------------------------------------------------------------------------------- 1 | package dserrors 2 | 3 | import "errors" 4 | 5 | var ( 6 | // ErrorBadDatasource is only returned when the plugin instance's type could not be asserted 7 | ErrorBadDatasource = errors.New("instance from plugin context is not a MongoDB Datasource") 8 | ) -------------------------------------------------------------------------------- /pkg/httputil/error.go: -------------------------------------------------------------------------------- 1 | package httputil 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | 7 | "github.com/grafana/grafana-plugin-sdk-go/backend/log" 8 | "github.com/pkg/errors" 9 | ) 10 | 11 | // WriteError writes an error in JSON format to the ResponseWriter 12 | func WriteError(w http.ResponseWriter, statusCode int, err error) { 13 | d := map[string]string{ 14 | "error": err.Error(), 15 | } 16 | 17 | b, marshalError := json.Marshal(d) 18 | if marshalError != nil { 19 | w.WriteHeader(http.StatusInternalServerError) 20 | if _, err := w.Write([]byte(errors.Wrapf(marshalError, "error when marshalling error '%s'", err.Error()).Error())); err != nil { 21 | log.DefaultLogger.Error(err.Error()) 22 | } 23 | return 24 | } 25 | 26 | w.WriteHeader(statusCode) 27 | if _, err := w.Write(b); err != nil { 28 | log.DefaultLogger.Error(err.Error()) 29 | } 30 | } 31 | 32 | // WriteResponse writes a standard HTTP response to the ResponseWriter in JSON format 33 | func WriteResponse(w http.ResponseWriter, data interface{}) { 34 | b, err := json.Marshal(data) 35 | if err != nil { 36 | WriteError(w, http.StatusInternalServerError, err) 37 | return 38 | } 39 | w.Header().Add("Content-Type", "application/json") 40 | if _, err := w.Write(b); err != nil { 41 | log.DefaultLogger.Error(err.Error()) 42 | } 43 | } -------------------------------------------------------------------------------- /pkg/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/plugin" 7 | "github.com/grafana/grafana-plugin-sdk-go/backend" 8 | "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" 9 | ) 10 | 11 | func main() { 12 | err := datasource.Serve(plugin.GetDatasourceOpts()) 13 | 14 | if err != nil { 15 | backend.Logger.Error(err.Error()) 16 | os.Exit(1) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /pkg/models/clusters.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type ListClustersOptions struct { 4 | Project string `json:"project"` 5 | } -------------------------------------------------------------------------------- /pkg/models/credentials.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/grafana/grafana-plugin-sdk-go/backend" 6 | ) 7 | 8 | type Settings struct { 9 | PublicKey string `json:"atlasPublicKey"` 10 | PrivateKey string `json:"atlasPrivateKey"` 11 | ApiType string `json:"apiType"` 12 | } 13 | 14 | func LoadSettings(settings backend.DataSourceInstanceSettings) (*Settings, error) { 15 | s := &Settings{} 16 | if err := json.Unmarshal(settings.JSONData, &s); err != nil { 17 | return &Settings{}, err 18 | } 19 | 20 | if val, ok := settings.DecryptedSecureJSONData["atlasPrivateKey"]; ok { 21 | s.PrivateKey = val 22 | } 23 | 24 | return s, nil 25 | } 26 | -------------------------------------------------------------------------------- /pkg/models/databases.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type ListDatabasesOptions struct { 4 | Project string `json:"project"` 5 | 6 | Mongo string `json:"mongo"` 7 | } -------------------------------------------------------------------------------- /pkg/models/disks.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type ListDisksOptions struct { 4 | Project string `json:"project"` 5 | 6 | Mongo string `json:"mongo"` 7 | } -------------------------------------------------------------------------------- /pkg/models/mongos.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type ListMongosOptions struct { 4 | Project string `json:"project"` 5 | } -------------------------------------------------------------------------------- /pkg/models/projects.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type ListProjectsOptions struct { 4 | } 5 | -------------------------------------------------------------------------------- /pkg/models/query.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | const ( 4 | QueryDiskMeasurements = "disk_measurements" 5 | QueryDatabaseMeasurements = "database_measurements" 6 | QueryProcessMeasurements = "process_measurements" 7 | ) 8 | 9 | type QueryOption struct { 10 | Label string `json:"label"` 11 | Value string `json:"value"` 12 | } 13 | 14 | type DiskMeasurementsQuery struct { 15 | Project QueryOption `json:"project"` 16 | 17 | Cluster QueryOption `json:"cluster"` 18 | 19 | Mongo QueryOption `json:"mongo"` 20 | 21 | Disk QueryOption `json:"disk"` 22 | 23 | Dimension QueryOption `json:"dimension"` 24 | 25 | Alias string `json:"alias"` 26 | 27 | RefId string `json:"refId"` 28 | } 29 | 30 | type ProcessMeasurementsQuery struct { 31 | Project QueryOption `json:"project"` 32 | 33 | Cluster QueryOption `json:"cluster"` 34 | 35 | Mongo QueryOption `json:"mongo"` 36 | 37 | Dimension QueryOption `json:"dimension"` 38 | 39 | Alias string `json:"alias"` 40 | 41 | RefId string `json:"refId"` 42 | } 43 | 44 | type DatabaseMeasurementsQuery struct { 45 | Project QueryOption `json:"project"` 46 | 47 | Cluster QueryOption `json:"cluster"` 48 | 49 | Mongo QueryOption `json:"mongo"` 50 | 51 | Database QueryOption `json:"database"` 52 | 53 | Dimension QueryOption `json:"dimension"` 54 | 55 | Alias string `json:"alias"` 56 | 57 | RefId string `json:"refId"` 58 | } 59 | 60 | type GetProcessMeasurementsQuery struct { 61 | Project string 62 | Mongo string 63 | Measurement string 64 | IntervalMs int64 65 | Start string 66 | End string 67 | } 68 | 69 | type GetDiskMeasurementsQuery struct { 70 | Project string 71 | Mongo string 72 | Disk string 73 | Measurement string 74 | IntervalMs int64 75 | Start string 76 | End string 77 | } 78 | 79 | type GetDatabaseMeasurementsQuery struct { 80 | Project string 81 | Mongo string 82 | Database string 83 | Measurement string 84 | IntervalMs int64 85 | Start string 86 | End string 87 | } 88 | -------------------------------------------------------------------------------- /pkg/plugin/datasource.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/dfutil" 7 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/models" 8 | "github.com/grafana/grafana-plugin-sdk-go/backend" 9 | ) 10 | 11 | type Datasource interface { 12 | HandleDatabaseMeasurementsQuery(context.Context, *models.DatabaseMeasurementsQuery, backend.DataQuery) (dfutil.Framer, error) 13 | HandleProcessMeasurementsQuery(context.Context, *models.ProcessMeasurementsQuery, backend.DataQuery) (dfutil.Framer, error) 14 | HandleDiskMeasurementsQuery(context.Context, *models.DiskMeasurementsQuery, backend.DataQuery) (dfutil.Framer, error) 15 | CheckHealth(context.Context) error 16 | } 17 | 18 | func HandleQueryData(ctx context.Context, d Datasource, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { 19 | m := GetQueryHandlers(&Server{ 20 | Datasource: d, 21 | }) 22 | 23 | return m.QueryData(ctx, req) 24 | } 25 | 26 | func CheckHealth(ctx context.Context, d Datasource, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) { 27 | if err := d.CheckHealth(ctx); err != nil { 28 | return &backend.CheckHealthResult{ 29 | Status: backend.HealthStatusError, 30 | Message: err.Error(), 31 | }, nil 32 | } 33 | return &backend.CheckHealthResult{ 34 | Status: backend.HealthStatusOk, 35 | Message: backend.HealthStatusOk.String(), 36 | }, nil 37 | } -------------------------------------------------------------------------------- /pkg/plugin/instance.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/dfutil" 7 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/datasource" 8 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/models" 9 | "github.com/grafana/grafana-plugin-sdk-go/backend" 10 | "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" 11 | ) 12 | 13 | // Instance is the root Datasource implementation that wraps a Datasource 14 | type Instance struct { 15 | Datasource Datasource 16 | Handlers Handlers 17 | } 18 | 19 | func (i *Instance) HandleDatabaseMeasurementsQuery(ctx context.Context, q *models.DatabaseMeasurementsQuery, req backend.DataQuery) (dfutil.Framer, error) { 20 | return i.Datasource.HandleDatabaseMeasurementsQuery(ctx, q, req) 21 | } 22 | 23 | func (i *Instance) HandleProcessMeasurementsQuery(ctx context.Context, q *models.ProcessMeasurementsQuery, req backend.DataQuery) (dfutil.Framer, error) { 24 | return i.Datasource.HandleProcessMeasurementsQuery(ctx, q, req) 25 | } 26 | 27 | func (i *Instance) HandleDiskMeasurementsQuery(ctx context.Context, q *models.DiskMeasurementsQuery, req backend.DataQuery) (dfutil.Framer, error) { 28 | return i.Datasource.HandleDiskMeasurementsQuery(ctx, q, req) 29 | } 30 | 31 | func (i *Instance) CheckHealth(ctx context.Context) error { 32 | return i.Datasource.CheckHealth(ctx) 33 | } 34 | 35 | // NewMongoDbAtlasInstance creates a new MongoDbAtlas using the settings to determine if things like the Caching Wrapper should be enabled 36 | func NewMongoDbAtlasInstance(ctx context.Context, settings *models.Settings) *Instance { 37 | var ( 38 | ds = datasource.NewDatasource(ctx, settings) 39 | ) 40 | 41 | var d Datasource = ds 42 | 43 | return &Instance{ 44 | Datasource: d, 45 | Handlers: Handlers{ 46 | Projects: ds.HandleGetProjects, 47 | Clusters: ds.HandleGetClusters, 48 | Databases: ds.HandleGetDatabases, 49 | Mongos: ds.HandleGetMongos, 50 | Disks: ds.HandleGetDisks, 51 | }, 52 | } 53 | } 54 | 55 | func newDataSourceInstance(settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { 56 | datasourceSettings, err := models.LoadSettings(settings) 57 | if err != nil { 58 | return nil, err 59 | } 60 | 61 | return NewMongoDbAtlasInstance(context.Background(), datasourceSettings), nil 62 | } 63 | -------------------------------------------------------------------------------- /pkg/plugin/plugin.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | dserrors "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/errors" 8 | "github.com/grafana/grafana-plugin-sdk-go/backend" 9 | "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" 10 | "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" 11 | "github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter" 12 | ) 13 | 14 | // Handler is the plugin entrypoint and implements all of the necessary handler functions for dataqueries, healthchecks, and resources. 15 | type Handler struct { 16 | // The instance manager can help with lifecycle management 17 | // of datasource instances in plugins. It's not a requirement 18 | // but a best practice that we recommend that you follow. 19 | im instancemgmt.InstanceManager 20 | } 21 | 22 | // QueryData handles multiple queries and returns multiple responses. 23 | // req contains the queries []DataQuery (where each query contains RefID as a unique identifer). 24 | // The QueryDataResponse contains a map of RefID to the response for each query, and each response 25 | // contains Frames ([]*Frame). 26 | func (cr *Handler) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { 27 | h, err := cr.im.Get(req.PluginContext) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | if val, ok := h.(*Instance); ok { 33 | return HandleQueryData(ctx, val, req) 34 | } 35 | return nil, dserrors.ErrorBadDatasource 36 | } 37 | 38 | // CheckHealth handles health checks sent from Grafana to the plugin. 39 | // The main use case for these health checks is the test button on the 40 | // datasource configuration page which allows users to verify that 41 | // a datasource is working as expected. 42 | func (cr *Handler) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) { 43 | h, err := cr.im.Get(req.PluginContext) 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | if val, ok := h.(*Instance); ok { 49 | return CheckHealth(ctx, val, req) 50 | } 51 | 52 | return &backend.CheckHealthResult{ 53 | Status: backend.HealthStatusError, 54 | Message: dserrors.ErrorBadDatasource.Error(), 55 | JSONDetails: nil, 56 | }, dserrors.ErrorBadDatasource 57 | } 58 | 59 | // ServeHTTP is the main HTTP handler for serving resource calls 60 | func (cr *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 61 | pluginCtx := httpadapter.PluginConfigFromContext(r.Context()) 62 | 63 | h, err := cr.im.Get(pluginCtx) 64 | if err != nil { 65 | panic(err) 66 | } 67 | 68 | if ds, ok := h.(*Instance); ok { 69 | GetRouter(ds.Handlers).ServeHTTP(w, r) 70 | return 71 | } 72 | 73 | panic(dserrors.ErrorBadDatasource) 74 | } 75 | 76 | // GetDatasourceOpts returns the datasource.ServeOpts for this plugin 77 | func GetDatasourceOpts() datasource.ServeOpts { 78 | handler := &Handler{ 79 | // creates a instance manager for your plugin. The function passed 80 | // into `NewInstanceManger` is called when the instance is created 81 | // for the first time or when a datasource configuration changed. 82 | im: datasource.NewInstanceManager(newDataSourceInstance), 83 | } 84 | 85 | return datasource.ServeOpts{ 86 | QueryDataHandler: handler, 87 | CheckHealthHandler: handler, 88 | CallResourceHandler: httpadapter.New(handler), 89 | } 90 | } -------------------------------------------------------------------------------- /pkg/plugin/query_database_measurements.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/dfutil" 7 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/models" 8 | "github.com/grafana/grafana-plugin-sdk-go/backend" 9 | ) 10 | 11 | func (s *Server) handleDatabaseMeasurementsQuery(ctx context.Context, q backend.DataQuery) backend.DataResponse { 12 | query := &models.DatabaseMeasurementsQuery{} 13 | if err := UnmarshalQuery(q.JSON, query); err != nil { 14 | return *err 15 | } 16 | return dfutil.FrameResponseWithError(s.Datasource.HandleDatabaseMeasurementsQuery(ctx, query, q)) 17 | } 18 | 19 | func (s *Server) HandleDatabaseMeasurements(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { 20 | return &backend.QueryDataResponse{ 21 | Responses: processQueries(ctx, req, s.handleDatabaseMeasurementsQuery), 22 | }, nil 23 | } -------------------------------------------------------------------------------- /pkg/plugin/query_disk_measurements.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/dfutil" 7 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/models" 8 | "github.com/grafana/grafana-plugin-sdk-go/backend" 9 | ) 10 | 11 | func (s *Server) handleDiskMeasurementsQuery(ctx context.Context, q backend.DataQuery) backend.DataResponse { 12 | query := &models.DiskMeasurementsQuery{} 13 | if err := UnmarshalQuery(q.JSON, query); err != nil { 14 | return *err 15 | } 16 | return dfutil.FrameResponseWithError(s.Datasource.HandleDiskMeasurementsQuery(ctx, query, q)) 17 | } 18 | 19 | func (s *Server) HandleDiskMeasurements(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { 20 | return &backend.QueryDataResponse{ 21 | Responses: processQueries(ctx, req, s.handleDiskMeasurementsQuery), 22 | }, nil 23 | } -------------------------------------------------------------------------------- /pkg/plugin/query_process_measurements.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/dfutil" 7 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/models" 8 | "github.com/grafana/grafana-plugin-sdk-go/backend" 9 | ) 10 | 11 | func (s *Server) handleProcessMeasurementsQuery(ctx context.Context, q backend.DataQuery) backend.DataResponse { 12 | query := &models.ProcessMeasurementsQuery{} 13 | if err := UnmarshalQuery(q.JSON, query); err != nil { 14 | return *err 15 | } 16 | return dfutil.FrameResponseWithError(s.Datasource.HandleProcessMeasurementsQuery(ctx, query, q)) 17 | } 18 | 19 | func (s *Server) HandleProcessMeasurements(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { 20 | return &backend.QueryDataResponse{ 21 | Responses: processQueries(ctx, req, s.handleProcessMeasurementsQuery), 22 | }, nil 23 | } -------------------------------------------------------------------------------- /pkg/plugin/resource_handlers.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gorilla/mux" 7 | ) 8 | 9 | // Handlers stores the list of http.HandlerFunc functions for the different resource calls 10 | type Handlers struct { 11 | Projects http.HandlerFunc 12 | Clusters http.HandlerFunc 13 | Mongos http.HandlerFunc 14 | Disks http.HandlerFunc 15 | Databases http.HandlerFunc 16 | } 17 | 18 | // GetRouter creates the gorilla/mux router for the HTTP handlers 19 | func GetRouter(h Handlers) *mux.Router { 20 | router := mux.NewRouter() 21 | router.Path("/projects").Methods("GET").HandlerFunc(h.Projects) 22 | router.Path("/clusters").Methods("GET").HandlerFunc(h.Clusters) 23 | router.Path("/mongos").Methods("GET").HandlerFunc(h.Mongos) 24 | router.Path("/disks").Methods("GET").HandlerFunc(h.Disks) 25 | router.Path("/databases").Methods("GET").HandlerFunc(h.Databases) 26 | 27 | return router 28 | } 29 | -------------------------------------------------------------------------------- /pkg/plugin/server.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | 7 | "github.com/valiton/grafana-mongodb-atlas-datasource/pkg/models" 8 | "github.com/grafana/grafana-plugin-sdk-go/backend" 9 | "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" 10 | "github.com/pkg/errors" 11 | ) 12 | 13 | // Server is the main handler for datasource queries. It forwards requests to the embedded datasource interface. 14 | type Server struct { 15 | Datasource Datasource 16 | } 17 | 18 | // QueryHandlerFunc is the function signature used for mux.HandleFunc 19 | type QueryHandlerFunc func(context.Context, backend.DataQuery) backend.DataResponse 20 | 21 | func processQueries(ctx context.Context, req *backend.QueryDataRequest, handler QueryHandlerFunc) backend.Responses { 22 | res := backend.Responses{} 23 | for _, v := range req.Queries { 24 | res[v.RefID] = handler(ctx, v) 25 | } 26 | 27 | return res 28 | } 29 | 30 | // UnmarshalQuery attempts to unmarshal a query from JSON 31 | func UnmarshalQuery(b []byte, v interface{}) *backend.DataResponse { 32 | if err := json.Unmarshal(b, v); err != nil { 33 | return &backend.DataResponse{ 34 | Error: errors.Wrap(err, "failed to unmarshal JSON request into query"), 35 | } 36 | } 37 | return nil 38 | } 39 | 40 | // GetQueryHandlers creates the QueryTypeMux type for handling queries 41 | func GetQueryHandlers(s *Server) *datasource.QueryTypeMux { 42 | mux := datasource.NewQueryTypeMux() 43 | 44 | // This could be a map[models.QueryType]datasource.QueryHandlerFunc and then a loop to handle all of them. 45 | mux.HandleFunc(models.QueryDatabaseMeasurements, s.HandleDatabaseMeasurements) 46 | mux.HandleFunc(models.QueryProcessMeasurements, s.HandleProcessMeasurements) 47 | mux.HandleFunc(models.QueryDiskMeasurements, s.HandleDiskMeasurements) 48 | 49 | return mux 50 | } -------------------------------------------------------------------------------- /src/ConfigEditor.tsx: -------------------------------------------------------------------------------- 1 | import React, { ChangeEvent, PureComponent } from 'react'; 2 | import { LegacyForms, RadioButtonGroup, Field, Label } from '@grafana/ui'; 3 | import { DataSourcePluginOptionsEditorProps } from '@grafana/data'; 4 | import { DataSourceOptions, SecureJsonData } from './types'; 5 | 6 | const { SecretFormField, FormField } = LegacyForms; 7 | 8 | interface Props extends DataSourcePluginOptionsEditorProps {} 9 | 10 | interface State {} 11 | 12 | export class ConfigEditor extends PureComponent { 13 | onPublicKeyChange = (event: any) => { 14 | const { onOptionsChange, options } = this.props; 15 | const jsonData = { 16 | ...options.jsonData, 17 | atlasPublicKey: event.target.value, 18 | }; 19 | onOptionsChange({ ...options, jsonData }); 20 | }; 21 | 22 | onApiTypeChange = (value: any) => { 23 | const { onOptionsChange, options } = this.props; 24 | const jsonData = { 25 | ...options.jsonData, 26 | apiType: value, 27 | }; 28 | onOptionsChange({ ...options, jsonData }); 29 | }; 30 | 31 | // Secure field (only sent to the backend) 32 | onPrivateKeyChange = (event: ChangeEvent) => { 33 | const { onOptionsChange, options } = this.props; 34 | onOptionsChange({ 35 | ...options, 36 | secureJsonData: { 37 | atlasPrivateKey: event.target.value, 38 | }, 39 | }); 40 | }; 41 | 42 | onResetPrivateKey = () => { 43 | const { onOptionsChange, options } = this.props; 44 | onOptionsChange({ 45 | ...options, 46 | secureJsonFields: { 47 | ...options.secureJsonFields, 48 | atlasPrivateKey: false, 49 | }, 50 | secureJsonData: { 51 | ...options.secureJsonData, 52 | atlasPrivateKey: '', 53 | }, 54 | }); 55 | }; 56 | 57 | render() { 58 | const { options } = this.props; 59 | const { jsonData, secureJsonFields } = options; 60 | const secureJsonData = (options.secureJsonData || {}) as SecureJsonData; 61 | 62 | return ( 63 | <> 64 |
65 | 77 |
78 | 84 |
85 | 86 |
87 | 96 |
97 |
98 | 99 |
100 |
101 | 105 | 113 | 114 |
115 |
116 | 117 | ); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/QueryEditor.tsx: -------------------------------------------------------------------------------- 1 | import { defaults } from 'lodash'; 2 | 3 | import React, { PureComponent } from 'react'; 4 | import {} from '@emotion/core'; // required for fix issue https://github.com/grafana/grafana/issues/26512 5 | import { SelectableValue, QueryEditorProps } from '@grafana/data'; 6 | import { SegmentAsync, Segment, Input, Icon, Tooltip } from '@grafana/ui'; 7 | import { DataSource } from './datasource'; 8 | import { defaultQuery, DataSourceOptions, Query, METRIC_TYPES, HUMANITZED_DIMENSIONS } from './types'; 9 | 10 | type Props = QueryEditorProps; 11 | 12 | export class QueryEditor extends PureComponent { 13 | isValid = () => { 14 | const query = defaults(this.props.query, defaultQuery); 15 | const { project, cluster, queryType, mongo, database, disk, dimension } = query; 16 | 17 | if (queryType === 'process_measurements') { 18 | return project && cluster && mongo && dimension; 19 | } else if (queryType === 'database_measurements') { 20 | return project && cluster && mongo && database && dimension; 21 | } else if (queryType === 'disk_measurements') { 22 | return project && cluster && disk && dimension; 23 | } else { 24 | return false; 25 | } 26 | }; 27 | 28 | onValueChange = (field: string) => (value: SelectableValue) => { 29 | const { onChange, query, onRunQuery } = this.props; 30 | onChange({ ...query, [field]: value }); 31 | if (this.isValid()) { 32 | onRunQuery(); 33 | } 34 | }; 35 | 36 | onQueryTypeChange = (value: SelectableValue) => { 37 | const { onChange, query, onRunQuery } = this.props; 38 | onChange({ ...query, queryType: value.value || '' }); 39 | if (this.isValid()) { 40 | onRunQuery(); 41 | } 42 | }; 43 | 44 | onAliasChange = (value: SelectableValue) => { 45 | const { onChange, query, onRunQuery } = this.props; 46 | if (!value.target.value) { 47 | return; 48 | } 49 | onChange({ ...query, alias: value.target.value }); 50 | if (this.isValid()) { 51 | onRunQuery(); 52 | } 53 | }; 54 | 55 | getProjects = async (): Promise>> => { 56 | const projects = await this.props.datasource.getProjects(); 57 | 58 | return projects.map(({ id, name }) => ({ 59 | value: id, 60 | label: name, 61 | })); 62 | }; 63 | 64 | getClusters = async (): Promise>> => { 65 | const clusters = await this.props.datasource.getClusters(this.props.query.project.value); 66 | 67 | return clusters.map(({ id, name }) => ({ 68 | value: id, 69 | label: name, 70 | })); 71 | }; 72 | 73 | getMongos = async (): Promise>> => { 74 | const mongos = await this.props.datasource.getMongos(this.props.query.project.value); 75 | 76 | return mongos.map(({ id, name }) => ({ 77 | value: id, 78 | label: name, 79 | })); 80 | }; 81 | 82 | getDatabases = async (): Promise>> => { 83 | const databases = await this.props.datasource.getDatabases( 84 | this.props.query.project.value, 85 | this.props.query.mongo.value 86 | ); 87 | 88 | return databases.map(name => ({ 89 | value: name, 90 | label: name, 91 | })); 92 | }; 93 | 94 | getDisks = async (): Promise>> => { 95 | const disks = await this.props.datasource.getDisks(this.props.query.project.value, this.props.query.mongo.value); 96 | 97 | return disks.map(name => ({ 98 | value: name, 99 | label: name, 100 | })); 101 | }; 102 | 103 | render() { 104 | const query = defaults(this.props.query, defaultQuery); 105 | const { alias, project, cluster, queryType, mongo, database, disk, dimension } = query; 106 | const metricsTypeValue = queryType; 107 | 108 | const showQueryTypes = project.value && cluster.value; 109 | const showMongoField = showQueryTypes; 110 | const showDatabaseField = showQueryTypes && metricsTypeValue === 'database_measurements'; 111 | const showDiskField = showQueryTypes && metricsTypeValue === 'disk_measurements'; 112 | const showDimensionsField = showQueryTypes; 113 | 114 | return ( 115 | <> 116 |
117 |
118 | Metric 119 | 120 | 126 | 127 | {project.value && ( 128 | 134 | )} 135 | 136 | {showQueryTypes && ( 137 | t.value === queryType) || METRIC_TYPES[0]} 139 | options={METRIC_TYPES} 140 | onChange={this.onQueryTypeChange} 141 | placeholder="select metric" 142 | /> 143 | )} 144 |
145 |
146 | 147 |
148 |
149 | Dimensions 150 | 151 | {showMongoField && ( 152 | 158 | )} 159 | 160 | {showDatabaseField && cluster && ( 161 | 167 | )} 168 | 169 | {showDiskField && cluster && ( 170 | 176 | )} 177 | 178 | {showDimensionsField && cluster && ( 179 | 185 | )} 186 |
187 |
188 | 189 |
190 |
191 | Alias 192 | 198 | 199 | 200 | 201 |
202 | } 203 | /> 204 |
205 | 206 | 207 | ); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/datasource.ts: -------------------------------------------------------------------------------- 1 | import { DataSourceWithBackend } from '@grafana/runtime'; 2 | import { DataSourceInstanceSettings } from '@grafana/data'; 3 | import { DataSourceOptions, Query, Project, Cluster, Mongo } from './types'; 4 | 5 | export class DataSource extends DataSourceWithBackend { 6 | constructor(instanceSettings: DataSourceInstanceSettings) { 7 | super(instanceSettings); 8 | } 9 | 10 | async getProjects(): Promise { 11 | return this.getResource('projects', {}); 12 | } 13 | 14 | async getClusters(projectId: string | undefined): Promise { 15 | if (projectId === undefined) { 16 | return []; 17 | } 18 | return this.getResource('clusters', { 19 | project: projectId, 20 | }); 21 | } 22 | 23 | async getMongos(projectId: string | undefined): Promise { 24 | if (projectId === undefined) { 25 | return []; 26 | } 27 | return this.getResource('mongos', { 28 | project: projectId, 29 | }); 30 | } 31 | 32 | async getDisks(projectId: string | undefined, mongoId: string | undefined): Promise { 33 | if (projectId === undefined || mongoId === undefined) { 34 | return []; 35 | } 36 | return this.getResource('disks', { 37 | project: projectId, 38 | mongo: mongoId, 39 | }); 40 | } 41 | 42 | async getDatabases(projectId: string | undefined, mongoId: string | undefined): Promise { 43 | if (projectId === undefined || mongoId === undefined) { 44 | return []; 45 | } 46 | return this.getResource('databases', { 47 | project: projectId, 48 | mongo: mongoId, 49 | }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/img/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/screenshots/datasource_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valiton/grafana-mongodb-atlas-datasource/16ab6f75e6de8780aa1f2488b56d3686588fa804/src/img/screenshots/datasource_list.png -------------------------------------------------------------------------------- /src/img/screenshots/datasource_setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valiton/grafana-mongodb-atlas-datasource/16ab6f75e6de8780aa1f2488b56d3686588fa804/src/img/screenshots/datasource_setup.png -------------------------------------------------------------------------------- /src/img/screenshots/query_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valiton/grafana-mongodb-atlas-datasource/16ab6f75e6de8780aa1f2488b56d3686588fa804/src/img/screenshots/query_example.png -------------------------------------------------------------------------------- /src/img/screenshots/query_setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valiton/grafana-mongodb-atlas-datasource/16ab6f75e6de8780aa1f2488b56d3686588fa804/src/img/screenshots/query_setup.png -------------------------------------------------------------------------------- /src/module.ts: -------------------------------------------------------------------------------- 1 | import { DataSourcePlugin } from '@grafana/data'; 2 | import { DataSource } from './datasource'; 3 | import { ConfigEditor } from './ConfigEditor'; 4 | import { QueryEditor } from './QueryEditor'; 5 | import { Query, DataSourceOptions } from './types'; 6 | 7 | export const plugin = new DataSourcePlugin(DataSource) 8 | .setConfigEditor(ConfigEditor) 9 | .setQueryEditor(QueryEditor); 10 | -------------------------------------------------------------------------------- /src/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/grafana/grafana/master/docs/sources/developers/plugins/plugin.schema.json", 3 | "type": "datasource", 4 | "name": "MongoDB Atlas Metrics", 5 | "id": "valiton-mongodbatlas-datasource", 6 | "metrics": true, 7 | "backend": true, 8 | "alerting": true, 9 | "executable": "gpx_mongodbatlas-datasource", 10 | "info": { 11 | "description": "Display MongoDB Atlas metrics inside of Grafana", 12 | "author": { 13 | "name": "Christoph Caprano", 14 | "url": "https://valiton.com" 15 | }, 16 | "keywords": ["mongodb", "atlas", "metrics"], 17 | "logos": { 18 | "small": "img/logo.svg", 19 | "large": "img/logo.svg" 20 | }, 21 | "links": [ 22 | { 23 | "name": "GitHub", 24 | "url": "https://github.com/valiton/grafana-mongodb-atlas-datasource" 25 | }, 26 | { 27 | "name": "MIT License", 28 | "url": "https://github.com/valiton/grafana-mongodb-atlas-datasource/blob/master/LICENSE" 29 | }, 30 | { 31 | "name": "Metrics Docs", 32 | "url": "https://docs.atlas.mongodb.com/api/" 33 | } 34 | ], 35 | "screenshots": [ 36 | { 37 | "name": "Setup", 38 | "path": "img/screenshots/datasource_setup.png" 39 | }, 40 | { 41 | "name": "Query Example", 42 | "path": "img/screenshots/query_example.png" 43 | }, 44 | { 45 | "name": "Query Options", 46 | "path": "img/screenshots/query_setup.png" 47 | } 48 | ], 49 | "version": "%VERSION%", 50 | "updated": "%TODAY%" 51 | }, 52 | "dependencies": { 53 | "grafanaDependency": ">=7.0.0", 54 | "plugins": [] 55 | } 56 | } -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { DataQuery, DataSourceJsonData, SelectableValue } from '@grafana/data'; 2 | 3 | import humanize from 'humanize-string'; 4 | 5 | export const DIMENSIONS = { 6 | process_measurements: [ 7 | 'ASSERT_REGULAR', 8 | 'ASSERT_WARNING', 9 | 'ASSERT_MSG', 10 | 'ASSERT_USER', 11 | 'CACHE_BYTES_READ_INTO', 12 | 'CACHE_BYTES_WRITTEN_FROM', 13 | 'CACHE_USAGE_DIRTY', 14 | 'CACHE_USAGE_USED', 15 | 'COMPUTED_MEMORY', 16 | 'CONNECTIONS', 17 | 'CONNECTIONS_MAX', 18 | 'CONNECTIONS_PERCENT', 19 | 'CURSORS_TOTAL_OPEN', 20 | 'CURSORS_TOTAL_TIMED_OUT', 21 | 'DB_STORAGE_TOTAL', 22 | 'DB_DATA_SIZE_TOTAL', 23 | 'DOCUMENT_METRICS_RETURNED', 24 | 'DOCUMENT_METRICS_INSERTED', 25 | 'DOCUMENT_METRICS_UPDATED', 26 | 'DOCUMENT_METRICS_DELETED', 27 | 'EXTRA_INFO_PAGE_FAULTS', 28 | 'GLOBAL_LOCK_CURRENT_QUEUE_TOTAL', 29 | 'GLOBAL_LOCK_CURRENT_QUEUE_READERS', 30 | 'GLOBAL_LOCK_CURRENT_QUEUE_WRITERS', 31 | 'LOGICAL_SIZE', 32 | 'MEMORY_RESIDENT', 33 | 'MEMORY_VIRTUAL', 34 | 'MEMORY_MAPPED', 35 | 'NETWORK_BYTES_IN', 36 | 'NETWORK_BYTES_OUT', 37 | 'NETWORK_NUM_REQUESTS', 38 | 'OPCOUNTER_CMD', 39 | 'OPCOUNTER_QUERY', 40 | 'OPCOUNTER_UPDATE', 41 | 'OPCOUNTER_DELETE', 42 | 'OPCOUNTER_GETMORE', 43 | 'OPCOUNTER_INSERT', 44 | 'OPCOUNTER_REPL_CMD', 45 | 'OPCOUNTER_REPL_UPDATE', 46 | 'OPCOUNTER_REPL_DELETE', 47 | 'OPCOUNTER_REPL_INSERT', 48 | 'OPERATIONS_SCAN_AND_ORDER', 49 | 'OP_EXECUTION_TIME_READS', 50 | 'OP_EXECUTION_TIME_WRITES', 51 | 'OP_EXECUTION_TIME_COMMANDS', 52 | 'OPLOG_MASTER_TIME', 53 | 'OPLOG_MASTER_LAG_TIME_DIFF', 54 | 'OPLOG_SLAVE_LAG_MASTER_TIME', 55 | 'OPLOG_RATE_GB_PER_HOUR', 56 | 'QUERY_EXECUTOR_SCANNED', 57 | 'QUERY_EXECUTOR_SCANNED_OBJECTS', 58 | 'QUERY_TARGETING_SCANNED_PER_RETURNED', 59 | 'QUERY_TARGETING_SCANNED_OBJECTS_PER_RETURNED', 60 | 'TICKETS_AVAILABLE_READS', 61 | 'TICKETS_AVAILABLE_WRITES', 62 | 'PROCESS_CPU_USER', 63 | 'PROCESS_CPU_KERNEL', 64 | 'PROCESS_CPU_CHILDREN_USER', 65 | 'PROCESS_CPU_CHILDREN_KERNEL', 66 | 'PROCESS_NORMALIZED_CPU_USER', 67 | 'PROCESS_NORMALIZED_CPU_KERNEL', 68 | 'PROCESS_NORMALIZED_CPU_CHILDREN_USER', 69 | 'PROCESS_NORMALIZED_CPU_CHILDREN_KERNEL', 70 | 'SWAP_USAGE_USED', 71 | 'SYSTEM_CPU_USER', 72 | 'SYSTEM_CPU_KERNEL', 73 | 'SYSTEM_CPU_NICE', 74 | 'SYSTEM_CPU_IOWAIT', 75 | 'SYSTEM_CPU_IRQ', 76 | 'SYSTEM_CPU_SOFTIRQ', 77 | 'SYSTEM_CPU_GUEST', 78 | 'SYSTEM_CPU_STEAL', 79 | 'SYSTEM_MEMORY_AVAILABLE', 80 | 'SYSTEM_NORMALIZED_CPU_USER', 81 | 'SYSTEM_NORMALIZED_CPU_KERNEL', 82 | 'SYSTEM_NORMALIZED_CPU_NICE', 83 | 'SYSTEM_NORMALIZED_CPU_IOWAIT', 84 | 'SYSTEM_NORMALIZED_CPU_IRQ', 85 | 'SYSTEM_NORMALIZED_CPU_SOFTIRQ', 86 | 'SYSTEM_NORMALIZED_CPU_GUEST', 87 | 'SYSTEM_NORMALIZED_CPU_STEAL', 88 | ], 89 | database_measurements: [ 90 | 'DATABASE_AVERAGE_OBJECT_SIZE', 91 | 'DATABASE_COLLECTION_COUNT', 92 | 'DATABASE_DATA_SIZE', 93 | 'DATABASE_STORAGE_SIZE', 94 | 'DATABASE_INDEX_SIZE', 95 | 'DATABASE_INDEX_COUNT', 96 | 'DATABASE_EXTENT_COUNT', 97 | 'DATABASE_OBJECT_COUNT', 98 | 'DATABASE_VIEW_COUNT', 99 | ], 100 | disk_measurements: [ 101 | 'DISK_PARTITION_IOPS_READ', 102 | 'DISK_PARTITION_IOPS_WRITE', 103 | 'DISK_PARTITION_IOPS_TOTAL', 104 | 'DISK_PARTITION_UTILIZATION', 105 | 'DISK_PARTITION_LATENCY_READ', 106 | 'DISK_PARTITION_LATENCY_WRITE', 107 | 'DISK_PARTITION_SPACE_FREE', 108 | 'DISK_PARTITION_SPACE_USED', 109 | 'DISK_PARTITION_SPACE_PERCENT_FREE', 110 | 'DISK_PARTITION_SPACE_PERCENT_USED', 111 | ], 112 | }; 113 | 114 | export const HUMANITZED_DIMENSIONS: Record>> = { 115 | disk_measurements: DIMENSIONS['disk_measurements'].map(dim => ({ 116 | value: dim, 117 | label: humanize(dim), 118 | })), 119 | database_measurements: DIMENSIONS['database_measurements'].map(dim => ({ 120 | value: dim, 121 | label: humanize(dim), 122 | })), 123 | process_measurements: DIMENSIONS['process_measurements'].map(dim => ({ 124 | value: dim, 125 | label: humanize(dim), 126 | })), 127 | }; 128 | 129 | export const METRIC_TYPES = Object.keys(DIMENSIONS).map(metric => ({ 130 | value: metric, 131 | label: humanize(metric), 132 | })); 133 | 134 | export const DEFAULT_PROJECT = { 135 | label: 'select project', 136 | value: '', 137 | }; 138 | 139 | export const DEFAULT_CLUSTER = { 140 | label: 'select cluster', 141 | value: '', 142 | }; 143 | 144 | export const DEFAULT_MONGO = { 145 | label: 'select mongo', 146 | value: '', 147 | }; 148 | 149 | export const DEFAULT_DISK = { 150 | label: 'select disk', 151 | value: '', 152 | }; 153 | 154 | export const DEFAULT_DATABASE = { 155 | label: '', 156 | value: '', 157 | }; 158 | 159 | export interface Query extends DataQuery { 160 | project: SelectableValue; 161 | cluster: SelectableValue; 162 | queryType: string; 163 | mongo: SelectableValue; 164 | database: SelectableValue; 165 | disk: SelectableValue; 166 | metric: SelectableValue; 167 | dimension: SelectableValue; 168 | alias: string; 169 | } 170 | 171 | export const defaultQuery: Partial = { 172 | project: DEFAULT_PROJECT, 173 | cluster: DEFAULT_CLUSTER, 174 | queryType: METRIC_TYPES[0].value, 175 | alias: '', 176 | }; 177 | 178 | /** 179 | * These are options configured for each DataSource instance 180 | */ 181 | export interface DataSourceOptions extends DataSourceJsonData { 182 | atlasPublicKey?: string; 183 | apiType?: string; 184 | } 185 | 186 | /** 187 | * Value that is used in the backend, but never sent over HTTP to the frontend 188 | */ 189 | export interface SecureJsonData { 190 | atlasPrivateKey?: string; 191 | } 192 | 193 | export interface Project { 194 | id: string; 195 | name: string; 196 | } 197 | 198 | export interface Cluster { 199 | id: string; 200 | name: string; 201 | } 202 | 203 | export interface Mongo { 204 | id: string; 205 | name: string; 206 | } 207 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@grafana/toolkit/src/config/tsconfig.plugin.json", 3 | "include": ["src", "types"], 4 | "compilerOptions": { 5 | "rootDir": "./src", 6 | "baseUrl": "./src", 7 | "typeRoots": ["./node_modules/@types"] 8 | } 9 | } --------------------------------------------------------------------------------