From 310f67bac484d47c4cf9f741c24e9ca43a6040c2 Mon Sep 17 00:00:00 2001 From: eduardo aleixo Date: Thu, 16 Mar 2023 18:06:02 +0000 Subject: [PATCH] fix: don't crash when an invalid `flamebearer` is passed (#10) Handles when the pyroscope panel is used alongside a different datasource, which previously would crash the panel, but now we display a better (but simple) message: Screenshot 2023-03-16 at 17 54 51 Also moves the code from the main repo to this one. --- .github/actions/lint-plugin/action.yaml | 26 ++++ .github/actions/package-plugin/action.yaml | 47 +++++++ .github/actions/setup-node/action.yaml | 38 ++++++ .github/workflows/ci.yml | 116 ++-------------- .github/workflows/conventional-commits.yaml | 20 +++ .github/workflows/cypress.yaml | 38 ++++++ .github/workflows/release-please.yaml | 14 ++ .github/workflows/release.yml | 142 +++----------------- .tool-versions | 2 + CHANGELOG.md | 18 +++ CONTRIBUTING.md | 24 ++++ README.md | 33 ++--- build-panel.sh | 44 ------ jest.config.js | 8 ++ lint.config.yaml | 15 +-- package.json | 17 ++- src/SimplePanel.module.css | 4 + src/SimplePanel.tsx | 44 ++++++ src/img/logo.svg | 32 +++++ src/module.ts | 42 ++++++ src/plugin.json | 35 +++++ src/styles.css | 11 ++ src/types.ts | 10 ++ tsconfig.json | 9 ++ yarn.lock | 135 +++++++++++++++++-- 25 files changed, 608 insertions(+), 316 deletions(-) create mode 100644 .github/actions/lint-plugin/action.yaml create mode 100644 .github/actions/package-plugin/action.yaml create mode 100644 .github/actions/setup-node/action.yaml create mode 100644 .github/workflows/conventional-commits.yaml create mode 100644 .github/workflows/cypress.yaml create mode 100644 .github/workflows/release-please.yaml create mode 100644 .tool-versions create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md delete mode 100755 build-panel.sh create mode 100644 jest.config.js create mode 100644 src/SimplePanel.module.css create mode 100644 src/SimplePanel.tsx create mode 100644 src/img/logo.svg create mode 100644 src/module.ts create mode 100644 src/plugin.json create mode 100644 src/styles.css create mode 100644 src/types.ts create mode 100644 tsconfig.json diff --git a/.github/actions/lint-plugin/action.yaml b/.github/actions/lint-plugin/action.yaml new file mode 100644 index 0000000..4ca6ebf --- /dev/null +++ b/.github/actions/lint-plugin/action.yaml @@ -0,0 +1,26 @@ +name: 'lint-plugin' +inputs: + enable-version-analyzer: + description: 'Whether to analyze the version or not' + required: true + file: + description: 'the plugin file (.zip)' + required: true +runs: + using: "composite" + steps: + - name: Lint plugin + shell: bash + run: | + export PATH="$(go env GOPATH)/bin/:$PATH" + envsubst < lint.config.yaml > lint.config.yaml + git clone https://github.com/grafana/plugin-validator + pushd ./plugin-validator/pkg/cmd/plugincheck2 + go install + popd + + plugincheck2 -strict -config lint.config.yaml -sourceCodeUri=file://./ ${{ inputs.file }} + env: + ENABLE_VERSION_ANALYZER: ${{ inputs.enable-version-analyzer }} + DEBUG: 1 + diff --git a/.github/actions/package-plugin/action.yaml b/.github/actions/package-plugin/action.yaml new file mode 100644 index 0000000..38aaf99 --- /dev/null +++ b/.github/actions/package-plugin/action.yaml @@ -0,0 +1,47 @@ +name: 'package-plugin' +inputs: + grafana-token: + description: 'Token to be used when signing (from grafana.com)' + required: true +outputs: + archive: + description: "The plugin zip file" + value: ${{ steps.metadata.outputs.archive }} + checksum: + description: "The checksum of the zip file" + value: ${{ steps.metadata.outputs.archive-checksum }} +runs: + using: "composite" + steps: + - run: yarn sign + shell: bash + env: + GRAFANA_API_KEY: ${{ inputs.grafana-token }} + - name: Get plugin metadata + id: metadata + shell: bash + run: | + sudo apt-get install jq + + export GRAFANA_PLUGIN_ID=$(cat dist/plugin.json | jq -r .id) + export GRAFANA_PLUGIN_VERSION=$(cat dist/plugin.json | jq -r .info.version) + export GRAFANA_PLUGIN_TYPE=$(cat dist/plugin.json | jq -r .type) + export GRAFANA_PLUGIN_ARTIFACT=${GRAFANA_PLUGIN_ID}-${GRAFANA_PLUGIN_VERSION}.zip + export GRAFANA_PLUGIN_ARTIFACT_CHECKSUM=${GRAFANA_PLUGIN_ARTIFACT}.md5 + + echo "::set-output name=plugin-id::${GRAFANA_PLUGIN_ID}" + echo "::set-output name=plugin-version::${GRAFANA_PLUGIN_VERSION}" + echo "::set-output name=plugin-type::${GRAFANA_PLUGIN_TYPE}" + echo "::set-output name=archive::${GRAFANA_PLUGIN_ARTIFACT}" + echo "::set-output name=archive-checksum::${GRAFANA_PLUGIN_ARTIFACT_CHECKSUM}" + + echo ::set-output name=github-tag::${GITHUB_REF#refs/*/} + + - name: Package plugin + id: packag- + shell: bash + run: | + mv dist ${{ steps.metadata.outputs.plugin-id }} + zip ${{ steps.metadata.outputs.archive }} ${{ steps.metadata.outputs.plugin-id }} -r + md5sum ${{ steps.metadata.outputs.archive }} > ${{ steps.metadata.outputs.archive-checksum }} + echo "::set-output name=checksum::$(cat ./${{ steps.metadata.outputs.archive-checksum }} | cut -d' ' -f1)" diff --git a/.github/actions/setup-node/action.yaml b/.github/actions/setup-node/action.yaml new file mode 100644 index 0000000..a6eb6f0 --- /dev/null +++ b/.github/actions/setup-node/action.yaml @@ -0,0 +1,38 @@ +name: 'setup-node' +description: 'Sets up nodejs' +runs: + using: "composite" + steps: + - name: Setup Node.js environment + uses: actions/setup-node@v2 + with: + node-version: "14.17" + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + shell: bash + + - name: Cache yarn cache + uses: actions/cache@v2 + id: cache-yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Cache node_modules + id: cache-node-modules + uses: actions/cache@v2 + with: + path: node_modules + key: ${{ runner.os }}-${{ matrix.node-version }}-node-modules-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.node-version }}-node-modules- + + - run: yarn --frozen-lockfile + shell: bash + - name: Build + run: yarn build + shell: bash diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c0b26c..d7aed1b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: CI +name: Lint env: CYPRESS_CACHE_FOLDER: cypress/cache @@ -8,111 +8,17 @@ on: branches: - main jobs: - build: + lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - - name: Setup Node.js environment - uses: actions/setup-node@v2 - with: - node-version: "14.17" - - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "::set-output name=dir::$(yarn cache dir)" - - - name: Cache yarn cache - uses: actions/cache@v2 - id: cache-yarn-cache - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: Cache node_modules - id: cache-node-modules - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.os }}-${{ matrix.node-version }}-node-modules-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.node-version }}-node-modules- - - - name: Pull dependencies - run: yarn - - name: Cache Cypress Binary - id: cache-cypress-binary - uses: actions/cache@v2 - with: - path: cypress/cache - key: cypress-binary-${{ hashFiles('yarn.lock') }} - - run: yarn cypress install - - name: Sign plugin - run: yarn sign - env: - GRAFANA_API_KEY: ${{ secrets.GRAFANA_API_KEY }} # Requires a Grafana API key from Grafana.com. - - # Smoke test - # We already tested it extensively in the other repository - - name: Run grafana server - run: docker-compose -f docker-compose.yml up -d - - - name: Run tests - run: yarn cy:ci - env: - CYPRESS_VIDEO: true - - uses: actions/upload-artifact@v2 - if: always() - with: - name: cypress-screenshots - path: pyroscope/cypress/screenshots - - uses: actions/upload-artifact@v2 - if: always() + - uses: actions/checkout@v3 + - uses: ./.github/actions/setup-node/ + - run: yarn sign + - uses: ./.github/actions/package-plugin/ + id: package-plugin with: - name: cypress-videos - path: pyroscope/cypress/videos - - # Setup the go environment, since the grafana plugin linter isn't distributed as a binary - - name: Setup Go environment - if: steps.check-for-backend.outputs.has-backend == 'true' - uses: actions/setup-go@v2 + grafana-token: ${{ secrets.GRAFANA_API_KEY }} + - uses: ./.github/actions/lint-plugin/ with: - go-version: "1.16" - - - name: Get plugin metadata - id: metadata - run: | - sudo apt-get install jq - - export GRAFANA_PLUGIN_ID=$(cat dist/plugin.json | jq -r .id) - export GRAFANA_PLUGIN_VERSION=$(cat dist/plugin.json | jq -r .info.version) - export GRAFANA_PLUGIN_TYPE=$(cat dist/plugin.json | jq -r .type) - export GRAFANA_PLUGIN_ARTIFACT=${GRAFANA_PLUGIN_ID}-${GRAFANA_PLUGIN_VERSION}.zip - export GRAFANA_PLUGIN_ARTIFACT_CHECKSUM=${GRAFANA_PLUGIN_ARTIFACT}.md5 - - echo "::set-output name=plugin-id::${GRAFANA_PLUGIN_ID}" - echo "::set-output name=plugin-version::${GRAFANA_PLUGIN_VERSION}" - echo "::set-output name=plugin-type::${GRAFANA_PLUGIN_TYPE}" - echo "::set-output name=archive::${GRAFANA_PLUGIN_ARTIFACT}" - echo "::set-output name=archive-checksum::${GRAFANA_PLUGIN_ARTIFACT_CHECKSUM}" - - echo ::set-output name=github-tag::${GITHUB_REF#refs/*/} - - - name: Package plugin - id: package-plugin - run: | - mv dist ${{ steps.metadata.outputs.plugin-id }} - zip ${{ steps.metadata.outputs.archive }} ${{ steps.metadata.outputs.plugin-id }} -r - md5sum ${{ steps.metadata.outputs.archive }} > ${{ steps.metadata.outputs.archive-checksum }} - echo "::set-output name=checksum::$(cat ./${{ steps.metadata.outputs.archive-checksum }} | cut -d' ' -f1)" - - - name: Lint plugin - run: | - export PATH="$(go env GOPATH)/bin/:$PATH" - - git clone https://github.com/grafana/plugin-validator - pushd ./plugin-validator/pkg/cmd/plugincheck2 - go install - popd - plugincheck2 -config lint.config.yaml ${{ steps.metadata.outputs.archive }} + file: ${{ steps.package-plugin.outputs.archive }} + enable-version-analyzer: false diff --git a/.github/workflows/conventional-commits.yaml b/.github/workflows/conventional-commits.yaml new file mode 100644 index 0000000..11c6037 --- /dev/null +++ b/.github/workflows/conventional-commits.yaml @@ -0,0 +1,20 @@ +name: conventional-pr +on: + pull_request: + branches: + - main + types: + - opened + - edited + - synchronize +jobs: + lint-pr: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: CondeNast/conventional-pull-request-action@v0.2.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + commitTitleMatch: "false" + ignoreCommits: "true" diff --git a/.github/workflows/cypress.yaml b/.github/workflows/cypress.yaml new file mode 100644 index 0000000..8172f4d --- /dev/null +++ b/.github/workflows/cypress.yaml @@ -0,0 +1,38 @@ +name: Cypress + +env: + CYPRESS_CACHE_FOLDER: cypress/cache + +on: + pull_request: + branches: + - main +jobs: + cypress: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/setup-node/ + - name: Cache Cypress Binary + id: cache-cypress-binary + uses: actions/cache@v2 + with: + path: cypress/cache + key: cypress-binary-${{ hashFiles('yarn.lock') }} + - run: yarn cypress install + - name: Run grafana server + run: docker-compose -f docker-compose.yml up -d + - name: Run tests + run: yarn cy:ci + env: + CYPRESS_VIDEO: true + - uses: actions/upload-artifact@v2 + if: always() + with: + name: cypress-screenshots + path: pyroscope/cypress/screenshots + - uses: actions/upload-artifact@v2 + if: always() + with: + name: cypress-videos + path: pyroscope/cypress/videos diff --git a/.github/workflows/release-please.yaml b/.github/workflows/release-please.yaml new file mode 100644 index 0000000..047ac91 --- /dev/null +++ b/.github/workflows/release-please.yaml @@ -0,0 +1,14 @@ +on: + push: + branches: + - main +name: release-please +jobs: + release-please: + runs-on: ubuntu-latest + steps: + - uses: google-github-actions/release-please-action@v3 + with: + release-type: node + package-name: release-please-action + token: ${{ secrets.RELEASE_PLEASE_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0d6464d..f49c6dc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,132 +1,26 @@ name: Release on: - push: - branches: - - main + release: + # https://stackoverflow.com/a/61066906 + types: [published] jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - - name: Setup Node.js environment - uses: actions/setup-node@v2 - with: - node-version: "14.17" - - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "::set-output name=dir::$(yarn cache dir)" - - - name: Cache yarn cache - uses: actions/cache@v2 - id: cache-yarn-cache - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: Cache node_modules - id: cache-node-modules - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.os }}-${{ matrix.node-version }}-node-modules-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.node-version }}-node-modules- - - name: Pull dependencies - run: yarn - - name: Cache Cypress Binary - id: cache-cypress-binary - uses: actions/cache@v2 - with: - path: cypress/cache - key: cypress-binary-${{ hashFiles('yarn.lock') }} - - run: yarn cypress install - - name: Sign plugin - run: yarn sign - env: - GRAFANA_API_KEY: ${{ secrets.GRAFANA_API_KEY }} # Requires a Grafana API key from Grafana.com. - - # Setup the go environment, since the grafana plugin linter isn't distributed as a binary - - name: Setup Go environment - if: steps.check-for-backend.outputs.has-backend == 'true' - uses: actions/setup-go@v2 - with: - go-version: "1.16" - - - name: Get plugin metadata - id: metadata - run: | - sudo apt-get install jq - - export GRAFANA_PLUGIN_ID=$(cat dist/plugin.json | jq -r .id) - export GRAFANA_PLUGIN_VERSION=$(cat dist/plugin.json | jq -r .info.version) - export GRAFANA_PLUGIN_TYPE=$(cat dist/plugin.json | jq -r .type) - export GRAFANA_PLUGIN_ARTIFACT=${GRAFANA_PLUGIN_ID}-${GRAFANA_PLUGIN_VERSION}.zip - export GRAFANA_PLUGIN_ARTIFACT_CHECKSUM=${GRAFANA_PLUGIN_ARTIFACT}.md5 - - echo "::set-output name=plugin-id::${GRAFANA_PLUGIN_ID}" - echo "::set-output name=plugin-version::${GRAFANA_PLUGIN_VERSION}" - echo "::set-output name=plugin-type::${GRAFANA_PLUGIN_TYPE}" - echo "::set-output name=archive::${GRAFANA_PLUGIN_ARTIFACT}" - echo "::set-output name=archive-checksum::${GRAFANA_PLUGIN_ARTIFACT_CHECKSUM}" - - echo ::set-output name=github-tag::${GITHUB_REF#refs/*/} - - - name: Package plugin + - uses: actions/checkout@v3 + - uses: ./.github/actions/setup-node/ + - run: yarn sign + - uses: ./.github/actions/package-plugin/ id: package-plugin - run: | - mv dist ${{ steps.metadata.outputs.plugin-id }} - zip ${{ steps.metadata.outputs.archive }} ${{ steps.metadata.outputs.plugin-id }} -r - md5sum ${{ steps.metadata.outputs.archive }} > ${{ steps.metadata.outputs.archive-checksum }} - echo "::set-output name=checksum::$(cat ./${{ steps.metadata.outputs.archive-checksum }} | cut -d' ' -f1)" - - - name: Lint plugin - run: | - export PATH="$(go env GOPATH)/bin/:$PATH" - - git clone https://github.com/grafana/plugin-validator - pushd ./plugin-validator/pkg/cmd/plugincheck2 - go install - popd - plugincheck2 -config lint.config.yaml ${{ steps.metadata.outputs.archive }} - # until here it's pretty much the same as ci.yml - # TODO: share the code somehow - - ########################## - # Release Specific Stuff # - ########################## - - name: Read changelog - id: changelog - run: | - awk '/^## / {s++} s == 1 {print}' pyroscope-panel/CHANGELOG.md > release_notes.md - echo "::set-output name=path::release_notes.md" - - - name: Create tag - uses: actions/github-script@v5 with: - script: | - github.rest.git.createRef({ - owner: context.repo.owner, - repo: context.repo.repo, - ref: 'refs/tags/v${{ steps.metadata.outputs.plugin-version }}', - sha: context.sha - }) - - - name: Create release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + grafana-token: ${{ secrets.GRAFANA_API_KEY }} + - uses: ./.github/actions/lint-plugin/ with: - tag_name: 'v${{ steps.metadata.outputs.plugin-version }}' - release_name: Release ${{ steps.metadata.outputs.plugin-version }} - body_path: ${{ steps.changelog.outputs.path }} - draft: false + file: ${{ steps.package-plugin.outputs.archive }} + enable-version-analyzer: true - name: Add plugin to release id: upload-plugin-asset @@ -134,9 +28,9 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./${{ steps.metadata.outputs.archive }} - asset_name: ${{ steps.metadata.outputs.archive }} + upload_url: ${{ github.event.release.upload_url }} + asset_path: ./${{ steps.package-plugin.outputs.archive }} + asset_name: ${{ steps.package-plugin.outputs.archive }} asset_content_type: application/zip - name: Add checksum to release @@ -145,12 +39,12 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./${{ steps.metadata.outputs.archive-checksum }} - asset_name: ${{ steps.metadata.outputs.archive-checksum }} + upload_url: ${{ github.event.release.upload_url }} + asset_path: ./${{ steps.package-plugin.outputs.checksum }} + asset_name: ${{ steps.package-plugin.outputs.checksum }} asset_content_type: text/plain - name: Publish to Grafana.com run: | - echo "A draft release has been created for your plugin. Please review and publish it." + echo "A release has been created for your plugin. Please review and publish it." echo "To publish a new version, follow https://github.com/grafana/grafana-plugin-repository#maintain-your-plugin" diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..c58772b --- /dev/null +++ b/.tool-versions @@ -0,0 +1,2 @@ +nodejs 16.18.0 +golang 1.19 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..de3ccf8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,18 @@ +# Changelog + +## [1.4.1] (2022-11-14) + +* support template variables +* Remove broken export button +* Improve flamegraph coloring for go + +## [1.1.0] +* Add context menu (triggered by right clicking the flamegraph) +* Add toolbar +* Allow focusing on a subtree +* Allow searching nodes that match a specific string +* Visual updates + +## [1.0.0] + +Initial release. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..31055b6 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,24 @@ +# Contributing + +## Releasing new versions +1. Merge the PR created by `Release Please` +2. Open [https://grafana.com/orgs/pyroscope/plugin-submissions/pyroscope-panel](https://grafana.com/orgs/pyroscope/plugin-submissions/pyroscope-datasource) and submit the plugin, copying the links from this repository's releases. + +## Fixing releases +After submitting to Grafana, they may find issues. + +If you fix these issues and publish a release, there will be a gap in grafana. +For example: +* Version 1.0.0 is already released and published to grafana +* You release a new version 1.1.0 +* Grafana plugin process finds an error +* You publish release version 1.1.1 +* Grafana plugin process approves that version +* Now there's a gap (`1.0.0` -> `1.1.1`), since `1.1.0` was skipped + +For that to not happen, you need to delete the previous release (1.1.0), so that it can be re-released. +To do that: +* Delete the release (via the ui) +* Delete the tag associated with that release (via the ui) +* Delete the commit Release Please uses (`git rebase -i MAIN ~2`, then pick `drop`). It requires force pushing to main, so enable that option for YOUR user only in the repo's settings. + diff --git a/README.md b/README.md index a5f517b..239992a 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,22 @@ # Pyroscope Grafana Panel Plugin -# Usage -For more info see [pyroscope-panel plugin for Grafana | Grafana Labs](https://grafana.com/grafana/plugins/pyroscope-panel/) -or the source code [pyroscope/packages/pyroscope-panel-plugin at main ยท pyroscope-io/pyroscope](https://github.com/pyroscope-io/pyroscope/tree/main/packages/pyroscope-panel-plugin) +**Important: Grafana version 8.0 or later required** +## Getting started -# Raison d'etre -The actual plugin development happens in the [main repo](https://github.com/pyroscope-io/pyroscope/tree/main/packages/pyroscope-panel-plugin). - -This repository is a simple wrapper around the `@pyroscope/panel-plugin` library. -It simply packages it, signs it and make a github release. -It does this for a couple reasons: -* So that we control the actual plugin release (since `@pyroscope/panel-plugin` release is automated) -* So that it's cleaner for users (The main repo releases refer to the pyroscope binary itself) - -# Releasing new versions -1. Update the `@pyroscope/panel-plugin` dependency -2. Open a Pull Request and merge it -3. Submit the new plugin version in Grafana Cloud (https://grafana.com/docs/grafana/latest/developers/plugins/package-a-plugin/#maintain-your-plugin) +1. Install the plugin (Installation tab) +2. Install [datasource plugin](https://grafana.com/grafana/plugins/pyroscope-datasource/) +3. Open Grafana and go to **Configuration -> Plugins** +4. Check that plugins are available +5. Set up data source plugin: + * **Configuration -> Data Sources -> Add data source** + * click on `pyroscope-datasource` + * Specify Pyroscope host in `Endpoint` field +6. Set up panel plugin: + * Add an empty panel on your dashboard + * Select `pyroscope-panel` from Visualization list + * Under panel view in Query tab select `pyroscope-datasource` + * In `Application name` input specify app name + * Click `Apply` +Congratulations! Now you can monitor application flamegraph on your Grafana dashboard! diff --git a/build-panel.sh b/build-panel.sh deleted file mode 100755 index a3ad2eb..0000000 --- a/build-panel.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -UPSTREAM_COMMIT="b30c01e72f7f26fb4494fb7dcab233dd4968095c" -PACKAGE_VERSION=$(cat package.json \ - | grep version \ - | head -1 \ - | awk -F: '{ print $2 }' \ - | sed 's/[",]//g' \ - | tr -d '[[:space:]]') - -cloneDst="pyroscope" - -################ -# Build Plugin # -################ -rm -Rf dist/ -rm -Rf "$cloneDst" - -#git clone --depth 1 --branch "$UPSTREAM_BRANCH" https://github.com/pyroscope-io/pyroscope.git "$cloneDst" -git clone --depth 1 --branch "main" https://github.com/pyroscope-io/pyroscope.git "$cloneDst" -git -C "$cloneDst" checkout "$UPSTREAM_COMMIT" - - - -# https://github.com/typicode/husky/issues/851 -# 'husky install' fails since there's no git -sed -i '/"prepare"/d' "$cloneDst/package.json" - -# install dependencies -yarn --cwd "$cloneDst" - - -# build panel -export PYROSCOPE_PANEL_VERSION="$PACKAGE_VERSION" -yarn --cwd "$cloneDst" build:panel - -cp -r "$cloneDst/grafana-plugin/panel/dist" dist - -################ -# Add metadata # -################ -cp CHANGELOG.md dist/ diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..bcf17c9 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,8 @@ +// This file is needed because it is used by vscode and other tools that +// call `jest` directly. However, unless you are doing anything special +// do not edit this file + +const standard = require('@grafana/toolkit/src/config/jest.plugin.config'); + +// This process will use the same config that `yarn test` is using +module.exports = standard.jestConfig(); diff --git a/lint.config.yaml b/lint.config.yaml index ae360ae..80f4ff0 100644 --- a/lint.config.yaml +++ b/lint.config.yaml @@ -1,17 +1,8 @@ global: enabled: true - severity: error jsonOutput: false + reportAll: true analyzers: - screenshots: - rules: - screenshots: - # TODO publish screenshots? - enabled: false - - pluginname: - rules: - # TODO update our name? - human-friendly-name: - enabled: false + version: + enabled: ${ENABLE_VERSION_ANALYZER} diff --git a/package.json b/package.json index 204e0d6..2727aab 100644 --- a/package.json +++ b/package.json @@ -1,22 +1,29 @@ { "name": "pyroscope-pyroscope-grafana", - "version": "0.0.1", + "version": "1.4.1", "description": "Pyroscope panel plugin for grafana", "scripts": { - "postinstall": "cp -r node_modules/@pyroscope/panel-plugin/dist .", "cy:open": "cypress open", "cy:ci": "cypress run", + "build": "grafana-toolkit plugin:build", + "test": "grafana-toolkit plugin:test", + "dev": "grafana-toolkit plugin:dev", + "watch": "grafana-toolkit plugin:dev --watch", "sign": "grafana-toolkit plugin:sign", - "cypress": "cypress" + "start": "yarn watch" }, "author": "Pyroscope devs", "license": "Apache-2.0", "engines": { "node": ">=14.17.0" }, - "dependencies": { + "devDependencies": { "@grafana/toolkit": "^8.3.2", - "@pyroscope/panel-plugin": "1.4.1", + "@pyroscope/datasource-plugin": "1.2.1", + "@types/lodash": "latest", "cypress": "^9.5.0" + }, + "dependencies": { + "@pyroscope/flamegraph": "^0.35.4" } } diff --git a/src/SimplePanel.module.css b/src/SimplePanel.module.css new file mode 100644 index 0000000..7732b44 --- /dev/null +++ b/src/SimplePanel.module.css @@ -0,0 +1,4 @@ +.panel { + overflow: auto; + height: 100%; +} diff --git a/src/SimplePanel.tsx b/src/SimplePanel.tsx new file mode 100644 index 0000000..bdfd1e5 --- /dev/null +++ b/src/SimplePanel.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { PanelProps, PanelData } from '@grafana/data'; +import { config } from '@grafana/runtime'; +import { FlamegraphRenderer } from '@pyroscope/flamegraph'; +import { SimpleOptions } from './types'; + +type Props = PanelProps; + +function extractFlamebearer( + data: PanelData +): { kind: 'ok'; flamebearer: React.ComponentProps['flamebearer'] } | { kind: 'error' } { + try { + const flamebearer = (data.series[data.series.length - 1].fields[0].values as any).buffer[0]; + + if (!flamebearer.names || !flamebearer.levels) { + return { kind: 'error' }; + } + + return { kind: 'ok', flamebearer }; + } catch (e) { + return { kind: 'error' }; + } +} +export const SimplePanel: React.FC = ({ options, data }) => { + const res = extractFlamebearer(data); + if (res.kind === 'error') { + return ( +
+
Invalid data. Make sure to use this panel with a Pyroscope datasource
+
+ ); + } + + return ( +
+ +
+ ); +}; diff --git a/src/img/logo.svg b/src/img/logo.svg new file mode 100644 index 0000000..84a0c05 --- /dev/null +++ b/src/img/logo.svg @@ -0,0 +1,32 @@ + + + logo@2x + + + + + + + + + + + diff --git a/src/module.ts b/src/module.ts new file mode 100644 index 0000000..cce8c16 --- /dev/null +++ b/src/module.ts @@ -0,0 +1,42 @@ +import { PanelPlugin } from '@grafana/data'; +import { loadPluginCss } from '@grafana/runtime'; +import { SimpleOptions } from './types'; +import { SimplePanel } from './SimplePanel'; +import '@pyroscope/flamegraph/dist/index.css'; +import './styles.css'; + +// Since our webpack config generates a single css file +// We have to load it somehow +// This could be solved differently, by using style-loader and injecting the css in the DOM using javascript +loadPluginCss({ + light: 'plugins/pyroscope-panel/module.css', + dark: 'plugins/pyroscope-panel/module.css', +}).catch(() => { + console.error('Failed to load CSS.'); +}); + +export const plugin = new PanelPlugin(SimplePanel).setPanelOptions((builder) => { + return builder + .addBooleanSwitch({ + description: + 'Whether to show the toolbar. Keep in mind most of the same functionality can be accessed by right-clicking the flamegraph.', + path: 'showToolbar', + name: 'Show toolbar', + defaultValue: false, + }) + .addSelect({ + path: 'displayOnly', + name: 'Display only', + description: 'Only display a single view, not allowing to change', + defaultValue: 'flamegraph', + settings: { + options: [ + { value: 'flamegraph', label: 'Flamegraph' }, + { value: 'table', label: 'Table' }, + { value: 'both', label: 'Flamegraph + Table' }, + { value: 'sandwich', label: 'Sandwich' }, + { value: 'off', label: 'Off' }, + ], + }, + }); +}); diff --git a/src/plugin.json b/src/plugin.json new file mode 100644 index 0000000..c619af2 --- /dev/null +++ b/src/plugin.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://raw.githubusercontent.com/grafana/grafana/master/docs/sources/developers/plugins/plugin.schema.json", + "type": "panel", + "name": "Pyroscope Flamegraph", + "id": "pyroscope-panel", + "info": { + "description": "Pyroscope plugin for grafana", + "author": { + "name": "Pyroscope", + "url": "https://pyroscope.io/" + }, + "keywords": ["callstack", "profiling"], + "logos": { + "small": "img/logo.svg", + "large": "img/logo.svg" + }, + "links": [ + { + "name": "Website", + "url": "https://pyroscope.io" + }, + { + "name": "License", + "url": "https://github.com/grafana/grafana-starter-panel/blob/master/LICENSE" + } + ], + "screenshots": [], + "version": "%VERSION%", + "updated": "%TODAY%" + }, + "dependencies": { + "grafanaDependency": ">=8.0.0", + "plugins": [] + } +} diff --git a/src/styles.css b/src/styles.css new file mode 100644 index 0000000..3241035 --- /dev/null +++ b/src/styles.css @@ -0,0 +1,11 @@ +/* TODO: move this to css modules */ +.flamegraph-wrapper { + height: 100%; + overflow-y: auto; + overflow-x: hidden; +} + +.panel { + overflow: auto; + height: 100%; +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..72e7973 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,10 @@ +type SeriesSize = 'sm' | 'md' | 'lg'; + +export interface SimpleOptions { + text: string; + showSeriesCount: boolean; + seriesCountSize: SeriesSize; + + showToolbar: boolean; + displayOnly: 'flamegraph' | 'table' | 'both' | 'sandwich' | 'off'; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..dcc552c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "rootDir": "./src", + "baseUrl": "./src", + "jsx": "react" + }, + "extends": "@grafana/toolkit/src/config/tsconfig.plugin.json", + "include": ["src", "types"] +} diff --git a/yarn.lock b/yarn.lock index 145cf7e..5d1b11c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1362,6 +1362,31 @@ uplot "1.6.22" xss "1.0.11" +"@grafana/data@8.5.21": + version "8.5.21" + resolved "https://registry.yarnpkg.com/@grafana/data/-/data-8.5.21.tgz#71f4653cd928af4b0aa27180e79ec4aa06508048" + integrity sha512-lJuwPxwzyE3vroQ9CqyiGc7PeTYHTPiVHKeJePQwFLl0F2XHf5y1v6VaCpb5SfGDmar1tcG8lrcc0KN4aplNfA== + dependencies: + "@braintree/sanitize-url" "6.0.0" + "@grafana/schema" "8.5.21" + "@types/d3-interpolate" "^1.4.0" + d3-interpolate "1.4.0" + date-fns "2.28.0" + eventemitter3 "4.0.7" + lodash "4.17.21" + marked "4.0.12" + moment "2.29.2" + moment-timezone "0.5.34" + ol "6.14.1" + papaparse "5.3.2" + react "17.0.2" + react-dom "17.0.2" + regenerator-runtime "0.13.9" + rxjs "7.5.5" + tslib "2.3.1" + uplot "1.6.22" + xss "1.0.11" + "@grafana/e2e-selectors@8.3.2": version "8.3.2" resolved "https://registry.yarnpkg.com/@grafana/e2e-selectors/-/e2e-selectors-8.3.2.tgz#6ff26e6c9de722f591f832cc5e887e296bfcc48d" @@ -1382,6 +1407,15 @@ tslib "2.3.1" typescript "4.4.4" +"@grafana/e2e-selectors@8.5.21": + version "8.5.21" + resolved "https://registry.yarnpkg.com/@grafana/e2e-selectors/-/e2e-selectors-8.5.21.tgz#6197351746dc0bcb90543d40ea04bb084b230a44" + integrity sha512-k7w5ohkeNrCNNMuoQCnIi3OS0BsFuVCCl7FY13o3hGWxH5WUDJhxIt80bkBZtQgJeKRb/izML3Sq5ggTA5fx5A== + dependencies: + "@grafana/tsconfig" "^1.2.0-rc1" + tslib "2.3.1" + typescript "4.4.4" + "@grafana/eslint-config@2.5.1": version "2.5.1" resolved "https://registry.yarnpkg.com/@grafana/eslint-config/-/eslint-config-2.5.1.tgz#93387e4e614412483436ec32fa759e5fb3f3b75e" @@ -1427,6 +1461,13 @@ dependencies: tslib "2.3.1" +"@grafana/schema@8.5.21": + version "8.5.21" + resolved "https://registry.yarnpkg.com/@grafana/schema/-/schema-8.5.21.tgz#5b9671861e460cb875b77485db1f4a6fde845e54" + integrity sha512-30aNWtxSoC96GBmVohGYFhtMXEtNmh7nGXK1BJBLMm1RxNxheNb1ym2WvMAGQP/J1J622EY/e+N65usAUltonw== + dependencies: + tslib "2.3.1" + "@grafana/slate-react@0.22.10-grafana": version "0.22.10-grafana" resolved "https://registry.yarnpkg.com/@grafana/slate-react/-/slate-react-0.22.10-grafana.tgz#53653bbef73530334a2db284129ccd0dfd682fef" @@ -1675,6 +1716,74 @@ uplot "1.6.22" uuid "8.3.2" +"@grafana/ui@^8.3.1": + version "8.5.21" + resolved "https://registry.yarnpkg.com/@grafana/ui/-/ui-8.5.21.tgz#000ca5bd288d6701d48b6fabcde959c3181a63bb" + integrity sha512-vMQ6RkInlvlMgOdsc830g9yQJ4TMZQFLKlTbfLFHe4j5Ij6n2sOhupaRUM447LHiINEQWAsoh93L/CL7gS8Sdw== + dependencies: + "@emotion/css" "11.7.1" + "@emotion/react" "11.8.2" + "@grafana/aws-sdk" "0.0.35" + "@grafana/data" "8.5.21" + "@grafana/e2e-selectors" "8.5.21" + "@grafana/schema" "8.5.21" + "@monaco-editor/react" "4.3.1" + "@popperjs/core" "2.11.4" + "@react-aria/button" "3.4.3" + "@react-aria/dialog" "3.1.8" + "@react-aria/focus" "3.5.4" + "@react-aria/menu" "3.4.3" + "@react-aria/overlays" "3.8.1" + "@react-stately/menu" "3.2.6" + "@sentry/browser" "6.19.1" + ansicolor "1.1.100" + calculate-size "1.1.1" + classnames "2.3.1" + core-js "3.21.1" + d3 "5.15.0" + date-fns "2.28.0" + hoist-non-react-statics "3.3.2" + immutable "4.0.0" + is-hotkey "0.2.0" + jquery "3.6.0" + lodash "4.17.21" + memoize-one "6.0.0" + moment "2.29.2" + monaco-editor "^0.31.1" + ol "6.14.1" + prismjs "1.27.0" + rc-cascader "3.4.1" + rc-drawer "4.4.3" + rc-slider "9.7.5" + rc-time-picker "^3.7.3" + react "17.0.2" + react-beautiful-dnd "13.1.0" + react-calendar "3.7.0" + react-colorful "5.5.1" + react-custom-scrollbars-2 "4.4.0" + react-dom "17.0.2" + react-dropzone "12.0.4" + react-highlight-words "0.17.0" + react-hook-form "7.5.3" + react-inlinesvg "2.3.0" + react-popper "2.2.5" + react-popper-tooltip "^4.3.1" + react-router-dom "^5.2.0" + react-select "5.2.2" + react-select-event "^5.1.0" + react-table "7.7.0" + react-transition-group "4.4.2" + react-use "17.3.2" + react-window "1.8.6" + rxjs "7.5.5" + slate "0.47.9" + slate-plain-serializer "0.7.11" + slate-react "0.22.10" + tinycolor2 "1.4.2" + tslib "2.3.1" + uplot "1.6.22" + uuid "8.3.2" + "@internationalized/date@3.0.0-alpha.1": version "3.0.0-alpha.1" resolved "https://registry.yarnpkg.com/@internationalized/date/-/date-3.0.0-alpha.1.tgz#987a86a98b837f275bce084ef502421bc5cdb5f7" @@ -2113,19 +2222,20 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45" integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw== -"@pyroscope/flamegraph@^0.30.0": - version "0.30.0" - resolved "https://registry.yarnpkg.com/@pyroscope/flamegraph/-/flamegraph-0.30.0.tgz#58a8ff50bce7b702368c90082bee1eb339f693a4" - integrity sha512-Hf1+S/4JxgnE/QZGauvU+N7kaDW1F2GJIJXylYSIPV2OoFnPS+gFWX4UZGxxJ1qehlrhjEV3LXk1xNNO9uPKYA== - -"@pyroscope/panel-plugin@1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@pyroscope/panel-plugin/-/panel-plugin-1.4.1.tgz#9e02dc994532be5c2ad0be3857e4f2baa03dca57" - integrity sha512-pbB7Hu/AO2VKnFXJ8CzXdkWLgKeD/7ZzQtoKzPoTZa4SEt0VeR3ReVXeyXPPuMcwtD44f58vvOtUpHHZRST8og== +"@pyroscope/datasource-plugin@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@pyroscope/datasource-plugin/-/datasource-plugin-1.2.1.tgz#91dc8700ba05363399a1b1b10cfc3f76db5fe455" + integrity sha512-N/o3w9JYfXny3b+GuduypnL5TRFoBfixKKTCyXSCOMObnN5Fn6v7k8iS+xvA0RZvigFFjjaW5JZUdnRbt1NAUQ== dependencies: "@grafana/data" "^8.3.1" "@grafana/runtime" "^8.3.1" - "@pyroscope/flamegraph" "^0.30.0" + "@grafana/ui" "^8.3.1" + lodash "^4.17.21" + +"@pyroscope/flamegraph@^0.35.4": + version "0.35.4" + resolved "https://registry.yarnpkg.com/@pyroscope/flamegraph/-/flamegraph-0.35.4.tgz#bb7d433d311d2b2cbb159c291d627c162095c503" + integrity sha512-xsUYLtO9kzdIU1UYoNQLhLNOfMtzwFyLYZEhTBs+XiDQfn9yoNj4kFbA9ms+CPJ5WOp6aITFQcmxv7EK/u2lgQ== "@react-aria/button@3.4.3": version "3.4.3" @@ -2920,6 +3030,11 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.149.tgz#1342d63d948c6062838fbf961012f74d4e638440" integrity sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ== +"@types/lodash@latest": + version "4.14.191" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.191.tgz#09511e7f7cba275acd8b419ddac8da9a6a79e2fa" + integrity sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ== + "@types/mime@^1": version "1.3.2" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"