├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── LICENSE ├── README.md ├── couchbase-datasource ├── .config │ ├── .cprc.json │ ├── .eslintrc │ ├── .prettierrc.js │ ├── Dockerfile │ ├── README.md │ ├── jest-setup.js │ ├── jest.config.js │ ├── jest │ │ ├── mocks │ │ │ └── react-inlinesvg.tsx │ │ └── utils.js │ ├── tsconfig.json │ ├── types │ │ └── custom.d.ts │ └── webpack │ │ ├── constants.ts │ │ ├── utils.ts │ │ └── webpack.config.ts ├── .eslintrc.yml ├── .gitignore ├── .nvmrc ├── .prettierrc.js ├── CHANGELOG.md ├── LICENSE ├── Magefile.go ├── README.md ├── docker-compose.yaml ├── go.mod ├── go.sum ├── jest-setup.js ├── jest.config.js ├── package.json ├── pkg │ ├── main.go │ └── plugin │ │ ├── plugin.go │ │ └── plugin_test.go ├── src │ ├── ConfigEditor.tsx │ ├── QueryEditor.tsx │ ├── datasource.ts │ ├── img │ │ ├── logo.png │ │ └── screenshot.png │ ├── module.ts │ ├── plugin.json │ └── types.ts ├── tsconfig.json └── yarn.lock ├── datasources └── couchbase.yaml ├── docker-compose.yaml ├── generate_data.sh ├── release.sh ├── res └── dashboards.png └── run.sh /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - master 8 | pull_request: 9 | branches: 10 | - master 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Setup Node.js environment 18 | uses: actions/setup-node@v2.1.2 19 | with: 20 | node-version: "14.x" 21 | 22 | - name: Get yarn cache directory path 23 | id: yarn-cache-dir-path 24 | run: echo "::set-output name=dir::$(yarn cache dir)" 25 | 26 | - name: Cache yarn cache 27 | uses: actions/cache@v4 28 | id: cache-yarn-cache 29 | with: 30 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 31 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 32 | restore-keys: | 33 | ${{ runner.os }}-yarn- 34 | 35 | - name: Cache node_modules 36 | id: cache-node-modules 37 | uses: actions/cache@v4 38 | with: 39 | path: node_modules 40 | key: ${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/yarn.lock') }} 41 | restore-keys: | 42 | ${{ runner.os }}-${{ matrix.node-version }}-nodemodules- 43 | 44 | - name: Install dependencies 45 | run: yarn install --frozen-lockfile 46 | 47 | - name: Build and test frontend 48 | run: yarn build 49 | 50 | - name: Check for backend 51 | id: check-for-backend 52 | run: | 53 | if [ -f "Magefile.go" ] 54 | then 55 | echo "::set-output name=has-backend::true" 56 | fi 57 | 58 | - name: Setup Go environment 59 | if: steps.check-for-backend.outputs.has-backend == 'true' 60 | uses: actions/setup-go@v2 61 | with: 62 | go-version: "1.16" 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 | -------------------------------------------------------------------------------- /.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 | workflow_dispatch: 8 | 9 | jobs: 10 | release: 11 | runs-on: ubuntu-latest 12 | 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: Setup Go environment 22 | uses: actions/setup-go@v2 23 | with: 24 | go-version: "1.15" 25 | 26 | - name: Get yarn cache directory path 27 | id: yarn-cache-dir-path 28 | run: echo "::set-output name=dir::$(yarn cache dir)" 29 | 30 | - name: Cache yarn cache 31 | uses: actions/cache@v2 32 | id: cache-yarn-cache 33 | with: 34 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 35 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 36 | restore-keys: | 37 | ${{ runner.os }}-yarn- 38 | 39 | - name: Cache node_modules 40 | id: cache-node-modules 41 | uses: actions/cache@v2 42 | with: 43 | path: node_modules 44 | key: ${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/yarn.lock') }} 45 | restore-keys: | 46 | ${{ runner.os }}-${{ matrix.node-version }}-nodemodules- 47 | 48 | - name: Install dependencies 49 | working-directory: couchbase-datasource 50 | run: yarn install --frozen-lockfile; 51 | if: | 52 | steps.cache-yarn-cache.outputs.cache-hit != 'true' || 53 | steps.cache-node-modules.outputs.cache-hit != 'true' 54 | 55 | - name: Build and test frontend 56 | working-directory: couchbase-datasource 57 | run: yarn build 58 | 59 | - name: Check for backend 60 | working-directory: couchbase-datasource 61 | id: check-for-backend 62 | run: | 63 | if [ -f "Magefile.go" ] 64 | then 65 | echo "::set-output name=has-backend::true" 66 | fi 67 | 68 | - name: Test backend 69 | working-directory: couchbase-datasource 70 | if: steps.check-for-backend.outputs.has-backend == 'true' 71 | uses: magefile/mage-action@v1 72 | with: 73 | version: latest 74 | args: coverage 75 | 76 | - name: Build backend 77 | working-directory: couchbase-datasource 78 | if: steps.check-for-backend.outputs.has-backend == 'true' 79 | uses: magefile/mage-action@v1 80 | with: 81 | version: latest 82 | args: buildAll 83 | 84 | - name: Sign plugin 85 | run: yarn sign 86 | enabled: false 87 | working-directory: couchbase-datasource 88 | env: 89 | GRAFANA_API_KEY: ${{ secrets.GRAFANA_API_KEY }} # Requires a Grafana API key from Grafana.com. 90 | 91 | - name: Get plugin metadata 92 | id: metadata 93 | working-directory: couchbase-datasource 94 | run: | 95 | sudo apt-get install jq 96 | 97 | export GRAFANA_PLUGIN_ID=$(cat dist/plugin.json | jq -r .id) 98 | export GRAFANA_PLUGIN_VERSION=$(cat dist/plugin.json | jq -r .info.version) 99 | export GRAFANA_PLUGIN_TYPE=$(cat dist/plugin.json | jq -r .type) 100 | export GRAFANA_PLUGIN_ARTIFACT=${GRAFANA_PLUGIN_ID}-${GRAFANA_PLUGIN_VERSION}.zip 101 | export GRAFANA_PLUGIN_ARTIFACT_CHECKSUM=${GRAFANA_PLUGIN_ARTIFACT}.md5 102 | 103 | echo "::set-output name=plugin-id::${GRAFANA_PLUGIN_ID}" 104 | echo "::set-output name=plugin-version::${GRAFANA_PLUGIN_VERSION}" 105 | echo "::set-output name=plugin-type::${GRAFANA_PLUGIN_TYPE}" 106 | echo "::set-output name=archive::${GRAFANA_PLUGIN_ARTIFACT}" 107 | echo "::set-output name=archive-checksum::${GRAFANA_PLUGIN_ARTIFACT_CHECKSUM}" 108 | 109 | echo ::set-output name=github-tag::${GITHUB_REF#refs/*/} 110 | 111 | - name: Read changelog 112 | id: changelog 113 | working-directory: couchbase-datasource 114 | run: | 115 | awk '/^## / {s++} s == 1 {print}' CHANGELOG.md > release_notes.md 116 | echo "::set-output name=path::release_notes.md" 117 | 118 | - name: Check package version 119 | working-directory: couchbase-datasource 120 | 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 121 | 122 | - name: Package plugin 123 | working-directory: couchbase-datasource 124 | id: package-plugin 125 | run: | 126 | mv dist ${{ steps.metadata.outputs.plugin-id }} 127 | zip ${{ steps.metadata.outputs.archive }} ${{ steps.metadata.outputs.plugin-id }} -r 128 | md5sum ${{ steps.metadata.outputs.archive }} > ${{ steps.metadata.outputs.archive-checksum }} 129 | echo "::set-output name=checksum::$(cat ./${{ steps.metadata.outputs.archive-checksum }} | cut -d' ' -f1)" 130 | 131 | - name: Lint plugin 132 | working-directory: couchbase-datasource 133 | run: | 134 | git clone https://github.com/grafana/plugin-validator 135 | pushd ./plugin-validator/pkg/cmd/plugincheck 136 | go install 137 | popd 138 | plugincheck ${{ steps.metadata.outputs.archive }} 139 | 140 | - name: Create release 141 | id: create_release 142 | working-directory: couchbase-datasource 143 | uses: actions/create-release@v1 144 | env: 145 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 146 | with: 147 | tag_name: ${{ github.ref }} 148 | release_name: Release ${{ github.ref }} 149 | body_path: ${{ steps.changelog.outputs.path }} 150 | draft: true 151 | 152 | - name: Add plugin to release 153 | id: upload-plugin-asset 154 | working-directory: couchbase-datasource 155 | uses: actions/upload-release-asset@v1 156 | env: 157 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 158 | with: 159 | upload_url: ${{ steps.create_release.outputs.upload_url }} 160 | asset_path: ./${{ steps.metadata.outputs.archive }} 161 | asset_name: ${{ steps.metadata.outputs.archive }} 162 | asset_content_type: application/zip 163 | 164 | - name: Add checksum to release 165 | id: upload-checksum-asset 166 | working-directory: couchbase-datasource 167 | uses: actions/upload-release-asset@v1 168 | env: 169 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 170 | with: 171 | upload_url: ${{ steps.create_release.outputs.upload_url }} 172 | asset_path: ./${{ steps.metadata.outputs.archive-checksum }} 173 | asset_name: ${{ steps.metadata.outputs.archive-checksum }} 174 | asset_content_type: text/plain 175 | 176 | - name: Publish to Grafana.com 177 | enabled: false 178 | run: | 179 | 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: 180 | echo 181 | 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 . 182 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2022 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Couchbase datasource plugin for Grafana 2 | 3 | !["Couchbase-powered dashboard"](res/dashboards.png "Example dashboard") 4 | 5 | This is a simple community-supported Grafana datasource plugin that allows querying time series and log data from Couchbase Clusters, inclding Capella clusters. 6 | 7 | ## Installation from sources 8 | This plugin uses [Standard Grafana DataSource Backend Plugin build process](https://grafana.com/developers/plugin-tools/tutorials/build-a-data-source-backend-plugin). Also, the `./run.sh` script in this repository is used to launch Grafana with the plugin in a Docker container and usually contains the latest build process commands. 9 | 10 | * Clone this repository 11 | * In `couchbase-datasource` directory, run `yarn install && yarn build && mage -v` 12 | * Copy `couchbase-datasource` directory into your Grafana `plugins` directory. 13 | * Set the following environment variables (or edit grafana configuration file according to documentation): 14 | - "GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS=couchbase-datasource" 15 | - "GF_PLUGIN_APP_TLS_SKIP_VERIFY_INSECURE=true" 16 | * Restart grafana 17 | * Add a new datasource in configuration and configure it with your cluster information. 18 | 19 | ## Usage 20 | The datasource supports only `SELECT` statements. 21 | The datasource plugin provides two additional sql++ `WHERE` clause functions that inject into all submitted queries time range filtering clauses according to 22 | selected in Grafana UI report time range: 23 | - `str_time_range()` for filtering on RFC3339 dates 24 | - `time_range()` for filtering on millisecond timestamps 25 | 26 | Both functions take the name of the field to be used for filtering. 27 | One and only one of these functions *must* be included in every query submitted through the plugin: 28 | Examples: 29 | 30 | ```sql 31 | select count, time_string from test where str_time_range(time_string) 32 | select count, timestamp from test where time_range(timestamp) 33 | ``` 34 | 35 | These are pseudo-functions, references to them are replaced with a set of `WHERE` filters on provided field by the plugin before the query is sent to the cluster. 36 | 37 | 38 | ## Development instructions 39 | Add `datasources/couchbase.yaml` with your datasource configuration: 40 | ```yaml 41 | apiVersion: 1 42 | datasources: 43 | - name: Couchbase 44 | type: couchbase-datasource 45 | access: proxy 46 | jsonData: 47 | host: <...> 48 | username: <...> 49 | secureJsonData: 50 | password: <...> 51 | ``` 52 | 53 | Use `./run.sh` to start grafana in docker container with following mounts: 54 | ```yaml 55 | volumes: 56 | - ./couchbase:/var/lib/grafana/plugins/couchbase 57 | - ./datasources:/etc/grafana/provisioning/datasources 58 | ``` 59 | 60 | Open grafana at http://localhost:3000, use `admin` as both login and password. 61 | You don't need to setup a new password after you login despite grafana asking you to do that -- just reload the page. 62 | 63 | --- 64 | 65 | ## 📢 Support Policy 66 | 67 | We truly appreciate your interest in this project! 68 | This project is **community-maintained**, which means it's **not officially supported** by our support team. 69 | 70 | If you need help, have found a bug, or want to contribute improvements, the best place to do that is right here — by [opening a GitHub issue](https://github.com/Couchbase-Ecosystem/grafana-plugin/issues). 71 | Our support portal is unable to assist with requests related to this project, so we kindly ask that all inquiries stay within GitHub. 72 | 73 | Your collaboration helps us all move forward together — thank you! 74 | -------------------------------------------------------------------------------- /couchbase-datasource/.config/.cprc.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.6.0" 3 | } 4 | -------------------------------------------------------------------------------- /couchbase-datasource/.config/.eslintrc: -------------------------------------------------------------------------------- 1 | /* 2 | * ⚠️⚠️⚠️ THIS FILE WAS SCAFFOLDED BY `@grafana/create-plugin`. DO NOT EDIT THIS FILE DIRECTLY. ⚠️⚠️⚠️ 3 | * 4 | * In order to extend the configuration follow the steps in 5 | * https://grafana.com/developers/plugin-tools/create-a-plugin/extend-a-plugin/extend-configurations#extend-the-eslint-config 6 | */ 7 | { 8 | "extends": ["@grafana/eslint-config"], 9 | "root": true, 10 | "rules": { 11 | "react/prop-types": "off" 12 | }, 13 | "overrides": [ 14 | { 15 | "plugins": ["deprecation"], 16 | "files": ["src/**/*.{ts,tsx}"], 17 | "rules": { 18 | "deprecation/deprecation": "warn" 19 | }, 20 | "parserOptions": { 21 | "project": "./tsconfig.json" 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /couchbase-datasource/.config/.prettierrc.js: -------------------------------------------------------------------------------- 1 | /* 2 | * ⚠️⚠️⚠️ THIS FILE WAS SCAFFOLDED BY `@grafana/create-plugin`. DO NOT EDIT THIS FILE DIRECTLY. ⚠️⚠️⚠️ 3 | * 4 | * In order to extend the configuration follow the steps in .config/README.md 5 | */ 6 | 7 | module.exports = { 8 | endOfLine: 'auto', 9 | printWidth: 120, 10 | trailingComma: 'es5', 11 | semi: true, 12 | jsxSingleQuote: false, 13 | singleQuote: true, 14 | useTabs: false, 15 | tabWidth: 2, 16 | }; 17 | -------------------------------------------------------------------------------- /couchbase-datasource/.config/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG grafana_version=latest 2 | ARG grafana_image=grafana-enterprise 3 | 4 | FROM grafana/${grafana_image}:${grafana_version} 5 | 6 | # Make it as simple as possible to access the grafana instance for development purposes 7 | # Do NOT enable these settings in a public facing / production grafana instance 8 | ENV GF_AUTH_ANONYMOUS_ORG_ROLE "Admin" 9 | ENV GF_AUTH_ANONYMOUS_ENABLED "true" 10 | ENV GF_AUTH_BASIC_ENABLED "false" 11 | # Set development mode so plugins can be loaded without the need to sign 12 | ENV GF_DEFAULT_APP_MODE "development" 13 | 14 | # Inject livereload script into grafana index.html 15 | USER root 16 | RUN sed -i 's/<\/body><\/html>/