Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dashboard Builder: Add capability to use Grafonnet #13

Merged
merged 1 commit into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
* Fix exit codes in failure situations.
* Fix exception handling and propagation in failure situations.
* Add feature to support dashboard builders, in the spirit of
dashboard-as-code.
dashboard-as-code. Supports Grafonnet, grafana-dashboard, grafanalib,
and any other kind of executable program generating Grafana Dashboard
JSON.
* Add watchdog feature, monitoring the input dashboard for changes on
disk, and re-uploading it, when changed.

Expand Down
60 changes: 56 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ _Export and import Grafana dashboards using the [Grafana HTTP API] and
- Export dashboards into JSON format.
- Import dashboards into Grafana, both in native JSON format, or
emitted by dashboard builders, supporting dashboard-as-code workflows.
- The import action preserves the version history of dashboards.
- Supported builders are [grafana-dashboard], [grafanalib], and
any other executable program which emits Grafana Dashboard JSON
- Supported builders are [Grafonnet], [grafana-dashboard], [grafanalib],
and any other executable program which emits Grafana Dashboard JSON
on STDOUT.
- The import action preserves the version history of dashboards.
- Watchdog: For a maximum of authoring and editing efficiency, the
watchdog monitors the input dashboard for changes on disk, and
re-uploads it to the Grafana API, when changed.
Expand All @@ -22,7 +22,7 @@ _Export and import Grafana dashboards using the [Grafana HTTP API] and
## Installation

```shell
pip install --upgrade 'grafana-import[builder] git+https://github.com/grafana-toolbox/grafana-import.git'
pip install --upgrade 'grafana-import[builder] @ git+https://github.com/grafana-toolbox/grafana-import.git'
```

Currently, there is no up-to-date version on PyPI, so we recommend to
Expand Down Expand Up @@ -169,6 +169,53 @@ using a configuration file, use the `GRAFANA_TOKEN` environment variable.
[grafana-client authentication variants]: https://github.com/panodata/grafana-client/#authentication


## Builders

grafana-import provides support for dashboard-as-code builders.

To get inspired what you can do, by reading more examples, please also visit
[grafonnet examples], [grafana-dashboard examples], and [grafanalib examples].

### Grafonnet

[Grafonnet] is a [Jsonnet] library for generating Grafana dashboards.

The library is generated from JSON Schemas generated by Grok. In turn,
these schemas are generated directly from the Grafana repository, in
order to ensure Grafonnet is always synchronized with the development
of Grafana without much friction.

#### Install
Install Jsonnet, and its jsonnet-bundler package manager.
```shell
brew install go-jsonnet jsonnet-bundler
```
Install Grafonnet into a Jsonnet project.
```shell
git clone https://github.com/grafana-toolbox/grafana-snippets
cd grafana-snippets/dashboard/grafonnet-simple
jb install github.com/grafana/grafonnet/gen/grafonnet-latest@main
```

#### Usage
Render dashboard defined in [Grafonnet]/[Jsonnet].
```shell
grafana-import import --overwrite -i /path/to/faro.jsonnet
```

### grafana-dashboard
Render dashboard defined using [grafana-dashboard].
```shell
grafana-import import --overwrite -i /path/to/gd-dashboard.py
```

### grafanalib
Render dashboard defined using [grafanalib].
```shell
grafana-import import --overwrite -i /path/to/gl-dashboard.py
```


## Help

`grafana-import --help`
Expand Down Expand Up @@ -238,4 +285,9 @@ learn how to set up a [development sandbox].
[Grafana HTTP API]: https://grafana.com/docs/grafana/latest/http_api/
[grafana-client]: https://github.com/panodata/grafana-client
[grafana-dashboard]: https://github.com/fzyzcjy/grafana_dashboard_python
[grafana-dashboard examples]: https://github.com/fzyzcjy/grafana_dashboard_python/tree/master/examples
[grafanalib]: https://github.com/weaveworks/grafanalib
[grafanalib examples]: https://github.com/weaveworks/grafanalib/tree/main/grafanalib/tests/examples
[Grafonnet]: https://github.com/grafana/grafonnet
[grafonnet examples]: https://github.com/grafana/grafonnet/tree/main/examples
[Jsonnet]: https://github.com/google/go-jsonnet
9 changes: 8 additions & 1 deletion docs/backlog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,11 @@
back to Python code. It should be used on the `export` subsystem,
to provide an alternative output format.
https://github.com/fzyzcjy/grafana_dashboard_python/tree/master/examples/json_to_python
- Provide support for the Grafana `cog` builder
- Builder: Support grafanalib
- https://community.panodata.org/t/grafanalib-is-a-python-library-for-building-grafana-dashboards/102
- Builder: Support Grafonnet
- https://github.com/grafana/grafonnet/
- https://community.panodata.org/t/grafonnet-a-jsonnet-library-for-generating-grafana-dashboards/158
- Builder: Support Grafana `cog`
- https://github.com/grafana/cog
- Slogan: Import and export Grafana dashboards ....
5 changes: 5 additions & 0 deletions grafana_import/util.py
Copy link
Collaborator

@peekjef72 peekjef72 Apr 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering about the security of calling a command line without checking its content ?
Should it increase user privileges ? if the command (grafana-import) has sudo rights it may cause pb !
It is especially for .py files (not jsonnet !)
Maybe a warning on this case should be set in the README...
It is a marginal case but...

Copy link
Contributor Author

@amotl amotl Apr 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi. I hear you on one hand, but on the other, giving a regular program like grafana-import which can do all sorts of things root permissions would be a bad idea per se, no?

If a user can run a (dashboard builder) program with python dashboard-builder.py, or if they do it through grafana-import, does not make any big difference, right?

It might be different when grafana-import would expose its functionality via a web service or similar means, where the user and the runtime are on different machines. But still in this case, I guess shlex.split() offers enough protective measures so it can't be exploited to invoke a different program, if you are referring to that?

So, if you don't mind, my personal evaluation of this is that shelling out like grafana-import is doing it, is safe enough for its purpose. If you really think it's not, or if anyone else from the community has any other objections or concerns, please let me know on behalf of a concrete example that demonstrates a relevant vulnerability, by creating a dedicated issue.

if the command (grafana-import) has sudo rights it may cause pb !

If you think I misunderstood your statement, where I translated "sudo rights" into "root permissions", please let me know. What does "pb" actually refer to?

Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ def read_dashboard_file(path: t.Union[str, Path]) -> t.Dict[str, t.Any]:
except OSError as ex:
raise IOError(f"Reading file failed: {path}. Reason: {ex.strerror}") from ex

elif path.suffix == ".jsonnet":
# jsonnet --jpath vendor faro.jsonnet
command = f"jsonnet --jpath {path.parent / 'vendor'} {path}"
payload = subprocess.check_output(shlex.split(command), encoding="utf-8") # noqa: S603

elif path.suffix == ".py":
command = f"{sys.executable} {path}"
payload = subprocess.check_output(shlex.split(command), encoding="utf-8") # noqa: S603
Expand Down
Loading