Docs: Plugin doc review for chunk 1-A (#67070)

* Plugin doc review for chunk 1-A

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* Group of fixes in response to review comments

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* Updates for review comments

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* Fixed extra space

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* Additional doc fixes

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* More doc fixes

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* Fixes from doc review

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* Doc review fixes

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* More changes from doc review

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* Fix links and relrefs using doc-validator advice

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* Publishing criteria update

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* Markdown fix

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* Fixes

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* Testing

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* Testing

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* Testing

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* Link fix

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* Prettier

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* Fix to publishing criteria

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* Doc fix

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* Doc fix

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* Doc fix

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

* Update docs/sources/developers/plugins/_index.md

Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com>

* Update docs/sources/developers/plugins/_index.md

Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com>

* Update docs/sources/developers/plugins/_index.md

Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com>

* Update docs/sources/developers/plugins/_index.md

Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com>

* Update docs/sources/developers/plugins/_index.md

Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com>

* Update docs/sources/developers/plugins/_index.md

Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com>

* Update docs/sources/developers/plugins/_index.md

Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com>

* Update docs/sources/developers/plugins/_index.md

Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com>

* Update docs/sources/developers/plugins/_index.md

Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com>

* Update docs/sources/developers/plugins/build-a-logs-data-source-plugin.md

Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com>

* Update docs/sources/developers/plugins/build-a-streaming-data-source-plugin.md

Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com>

* Update docs/sources/developers/plugins/build-a-streaming-data-source-plugin.md

Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com>

* Update docs/sources/developers/plugins/get-started-with-plugins/_index.md

Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com>

* Update docs/sources/developers/plugins/publish-a-plugin.md

Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com>

* Update docs/sources/developers/plugins/publish-a-plugin.md

Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com>

* Update docs/sources/developers/plugins/publish-a-plugin.md

Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com>

* Update docs/sources/developers/plugins/sign-a-plugin.md

Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com>

* Prettier

Signed-off-by: Joe Perez <joseph.perez@grafana.com>

---------

Signed-off-by: Joe Perez <joseph.perez@grafana.com>
Signed-off-by: Jack Baldry <jack.baldry@grafana.com>
Co-authored-by: Jack Baldry <jack.baldry@grafana.com>
Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com>
This commit is contained in:
Joseph Perez 2023-05-08 14:33:39 -07:00 committed by GitHub
parent 24668137f8
commit 13be068919
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 315 additions and 322 deletions

View File

@ -2,81 +2,51 @@
aliases:
- ../plugins/developing/
description: Resources for creating Grafana plugins
title: Build a plugin
title: Plugin developer's guide
weight: 200
---
# Build a plugin
# Grafana plugin developer's guide
For more information on the types of plugins you can build, refer to the [Plugin Overview]({{< relref "../../administration/plugin-management/" >}}).
You can extend Grafana's built-in capabilities with plugins. Plugins enable Grafana to accomplish specialized tasks, custom-tailored to your requirements. By making a plugin for your organization, you can connect Grafana to other data sources, ticketing tools, and CI/CD tooling.
## Get started
You can create plugins for private use or contribute them to the open source community by publishing to the [Grafana plugin catalog](https://grafana.com/grafana/plugins/). This catalog has hundreds of other community and commercial plugins.
The easiest way to start developing Grafana plugins is to use the Grafana [create-plugin tool](https://www.npmjs.com/package/@grafana/create-plugin).
If you are a Grafana plugin developer or want to become one, this plugin developer's guide contains the tutorials and reference materials to help you get started.
Open the terminal, and run the following command in your [plugin directory]({{< relref "../../setup-grafana/configure-grafana/#plugins" >}}):
## Plugin basics
```bash
npx @grafana/create-plugin@latest
```
You can create several types of plugins, including:
Follow the questions and you will have a starter plugin ready to develop.
- **Panel plugins** - Visualize data and navigate between dashboards.
- **Data source plugins** - Link to new databases or other sources of data.
- **App plugins** - Create rich applications for custom out-of-the-box experiences.
If you want a more guided introduction to plugin development, check out our tutorials:
> **Note:** To learn more about the types of plugins you can build, refer to the [Plugin management]({{< relref "../../administration/plugin-management" >}}) documentation.
- [Build a panel plugin](/tutorials/build-a-panel-plugin/)
- [Build a data source plugin](/tutorials/build-a-data-source-plugin/)
## Contents of this developer's guide
The following topics are covered in this guide:
- **[Get started with plugins]({{< relref "./get-started-with-plugins" >}})** - Start developing Grafana plugins with the [Create-plugin](https://www.npmjs.com/package/@grafana/create-plugin) tool.
- **[Introduction to plugin development]({{< relref "./introduction-to-plugin-development" >}})** - Learn the fundamentals of Grafana plugin development: frontend and backend development processes, data frames, error handling, and more.
- **[Create a Grafana plugin]({{< relref "./create-a-grafana-plugin" >}})** - If you're familiar with plugin creation, use the tutorials for creating panel plugins, data source plugins, and more to deepen your knowledge.
- **[Migrate a plugin]({{< relref "./migration-guide" >}})** - Learn how to upgrade from a previous version of a Grafana plugin, rewrite an old Angular plugin in React, or update to a newer version.
- **[Publish a Grafana plugin]({{< relref "./publish-a-plugin" >}})** - Learn about publishing a plugin to the Grafana plugin catalog, including publishing criteria, packaging, and deployment.
- **[Reference]({{< relref "metadata.md" >}})** - Description of the `plugin.json` schema and plugin metadata.
## Go further
Learn more about specific areas of plugin development.
Learn more about additional tools and see plugin type examples.
### Tutorials
If you're looking to build your first plugin, check out these introductory tutorials:
- [Build a panel plugin](/tutorials/build-a-panel-plugin/)
- [Build a data source plugin](/tutorials/build-a-data-source-plugin/)
- [Build a data source backend plugin](/tutorials/build-a-data-source-backend-plugin/)
Ready to learn more? Check out our other tutorials:
- [Build a panel plugin with D3.js](/tutorials/build-a-panel-plugin-with-d3/)
### Guides
Improve an existing plugin with one of our guides:
- [Add authentication for data source plugins]({{< relref "add-authentication-for-data-source-plugins.md" >}})
- [Add support for annotations]({{< relref "add-support-for-annotations.md" >}})
- [Add support for Explore queries]({{< relref "add-support-for-explore-queries.md" >}})
- [Add support for variables]({{< relref "add-support-for-variables.md" >}})
- [Add a query editor help component]({{< relref "add-query-editor-help.md" >}})
- [Add distributed tracing for backend plugins]({{< relref "add-distributed-tracing-for-backend-plugins.md" >}})
- [Build a logs data source plugin]({{< relref "build-a-logs-data-source-plugin.md" >}})
- [Build a streaming data source plugin]({{< relref "build-a-streaming-data-source-plugin.md" >}}/)
- [Error handling]({{< relref "error-handling.md" >}})
- [Working with data frames]({{< relref "working-with-data-frames.md" >}})
- [Development with local Grafana]({{< relref "development-with-local-grafana.md" >}})
### Concepts
Deepen your knowledge through a series of high-level overviews of plugin concepts:
- [Data frames]({{< relref "data-frames.md" >}})
### UI library
### User interface creation
Explore the many UI components in our [Grafana UI library](https://developers.grafana.com/ui).
### Examples
### Plugin examples
For inspiration, check out our [plugin examples](https://github.com/grafana/grafana-plugin-examples).
### Metadata
- [Plugin metadata]({{< relref "metadata.md" >}})
Grafana Labs provides a number of best practice example plugins for different use cases to help you quickly get started. Browse our [plugin examples](https://github.com/grafana/grafana-plugin-examples).
### SDK
- [Grafana Plugin SDK for Go]({{< relref "backend/grafana-plugin-sdk-for-go.md" >}})
Learn more about [Grafana Plugin SDK for Go]({{< relref "./backend/grafana-plugin-sdk-for-go" >}}).

View File

@ -4,19 +4,22 @@ title: Build a logs data source plugin
# Build a logs data source plugin
This guide explains how to build a logs data source plugin.
Grafana data source plugins support metrics, logs, and other data types. The steps to build a logs data source plugin are largely the same as for a metrics data source, but there are a few differences which we will explain in this guide.
Data sources in Grafana supports both metrics and log data. The steps to build a logs data source plugin are largely the same as for a metrics data source. This guide assumes that you're already familiar with how to [Build a data source plugin](/tutorials/build-a-data-source-plugin/) for metrics.
## Before you begin
This guide assumes that you're already familiar with how to [Build a data source plugin](/tutorials/build-a-data-source-plugin/) for metrics. We recommend that you review this material before continuing.
## Add logs support to your data source
To add logs support to an existing data source, you need to:
- Enable logs support
- Construct the log data
- (Optional) Add preferred visualisation type hint to the data frame
1. Enable logs support
1. Construct the log data
### Enable logs support
When these steps are done, then you can improve the user experience with one or more [optional features](#enhance-your-logs-data-source-plugin-with-optional-features).
### Step 1: Enable logs support
Tell Grafana that your data source plugin can return log data, by adding `"logs": true` to the [plugin.json]({{< relref "metadata/" >}}) file.
@ -26,9 +29,9 @@ Tell Grafana that your data source plugin can return log data, by adding `"logs"
}
```
### Construct the log data
### Step 2: Construct the log data
Just like for metrics data, Grafana expects your plugin to return log data as a [data frame]({{< relref "data-frames/" >}}).
As it does with metrics data, Grafana expects your plugin to return log data as a [data frame]({{< relref "data-frames/" >}}).
To return log data, return a data frame with at least one time field and one text field from the data source's `query` method.
@ -51,9 +54,13 @@ That's all you need to start returning log data from your data source. Go ahead
Congratulations, you just wrote your first logs data source plugin! Next, let's look at a couple of features that can further improve the experience for the user.
### (Optional) Add preferred visualisation type hint to the data frame
## Enhance your logs data source plugin with optional features
To make sure Grafana recognizes data as logs and shows logs visualization automatically in Explore you have do set `meta.preferredVisualisationType` to `'logs'` in the returned data frame. See [Selecting preferred visualisation section]({{< relref "add-support-for-explore-queries/#selecting-preferred-visualisation" >}})
Add visualization type hints, labels, and other optional features to logs.
### Add a preferred visualization type hint to the data frame
To make sure Grafana recognizes data as logs and shows logs visualization automatically in Explore, set `meta.preferredVisualisationType` to `'logs'` in the returned data frame. See [Selecting preferred visualisation section]({{< relref "add-support-for-explore-queries/#selecting-preferred-visualisation" >}})
**Example:**
@ -70,11 +77,11 @@ const frame = new MutableDataFrame({
});
```
## Add labels to your logs
### Add labels to your logs
To help filter log lines, many log systems let you query logs based on metadata, or _labels_.
Many log systems let you query logs based on metadata, or _labels_, to help filter log lines.
You can add labels to a stream of logs by setting the labels property on the Field.
Add labels to a stream of logs by setting the `labels` property on the Field.
**Example**:
@ -91,15 +98,15 @@ frame.add({ time: 1589189388597, content: 'user registered' });
frame.add({ time: 1589189406480, content: 'user logged in' });
```
## Extract detected fields from your logs
### Extract detected fields from your logs
You can add additional information about each log line by adding more data frame fields.
Add additional information about each log line by supplying more data frame fields.
If a data frame has more than one text field, then Grafana assumes the first field in the data frame to be the actual log line. Any subsequent text fields are treated as [detected fields]({{< relref "../../explore/#labels-and-detected-fields" >}}).
If a data frame has more than one text field, then Grafana assumes the first field in the data frame to be the actual log line. Grafana treats subsequent text fields as [detected fields]({{< relref "../../explore/#labels-and-detected-fields" >}}).
While you can add any number of custom fields to your data frame, Grafana comes with a couple of dedicated fields: `levels` and `id`. Let's have a closer look at each one.
Any number of custom fields can be added to your data frame; Grafana comes with two dedicated fields: `levels` and `id`.
### Levels
#### Levels
To set the level for each log line, add a `level` field.
@ -119,7 +126,7 @@ frame.add({ time: 1589189388597, content: 'user registered', level: 'info' });
frame.add({ time: 1589189406480, content: 'unknown error', level: 'error' });
```
### Unique log lines
#### 'id' for assigning unique identifiers to log lines
By default, Grafana offers basic support for deduplicating log lines. You can improve the support by adding an `id` field to explicitly assign identifiers to each log line.

View File

@ -4,100 +4,112 @@ title: Build a streaming data source plugin
# Build a streaming data source plugin
This guide explains how to build a streaming data source plugin.
In Grafana, you can set your dashboards to automatically refresh at a certain interval, no matter what data source you use. Unfortunately, this means that your queries are requesting all the data to be sent again, regardless of whether the data has actually changed. Adding streaming to a plugin helps reduce queries so your dashboard is only updated when new data becomes available.
## Before you begin
This guide assumes that you're already familiar with how to [Build a data source plugin](/tutorials/build-a-data-source-plugin/).
When monitoring critical applications, you want your dashboard to refresh as soon as your data does. In Grafana, you can set your dashboards to automatically refresh at a certain interval, no matter what data source you use. Unfortunately, this means that your queries are requesting all the data to be sent again, regardless of whether the data has actually changed.
Grafana uses [RxJS](https://rxjs.dev/) to continuously send data from a data source to a panel visualization.
By enabling _streaming_ for your data source plugin, you can update your dashboard as soon as new data becomes available.
> **Note:** To learn more about RxJs, refer to the [RxJS documentation](https://rxjs.dev/guide/overview).
## Add streaming to your data source
Enable streaming for your data source plugin to update your dashboard when new data becomes available.
For example, a streaming data source plugin can connect to a websocket, or subscribe to a message bus, and update the visualization whenever a new message is available.
Let's see how you can add streaming to an existing data source!
### Step 1: Edit the `plugin.json` file
Grafana uses [RxJS](https://rxjs.dev/) to continuously send data from a data source to a panel visualization. There's a lot more to RxJS than what's covered in this guide. If you want to learn more, check out the [RxJS documentation](https://rxjs.dev/guide/overview).
Enable streaming for your data source in the `plugin.json` file.
1. Enable streaming for your data source in the `plugin.json` file.
```json
{
"streaming": true
}
```
```json
{
"streaming": true
}
```
### Step 2: Change the signature of the `query` method
1. Change the signature of the `query` method to return an `Observable` from the `rxjs` package. Make sure you remove the `async` keyword.
Modify the signature of the `query` method to return an `Observable` from the `rxjs` package. Make sure you remove the `async` keyword.
```ts
import { Observable } from 'rxjs';
```
```ts
import { Observable } from 'rxjs';
```
```ts
query(options: DataQueryRequest<MyQuery>): Observable<DataQueryResponse> {
// ...
}
```
```ts
query(options: DataQueryRequest<MyQuery>): Observable<DataQueryResponse> {
// ...
}
```
1. Create an `Observable` for each query, and then combine them all using the `merge` function from the `rxjs` package.
### Step 3: Create an `Observable` instance for each query
```ts
import { Observable, merge } from 'rxjs';
```
Create an `Observable` instance for each query, and then combine them all using the `merge` function from the `rxjs` package.
```ts
const observables = options.targets.map((target) => {
return new Observable<DataQueryResponse>((subscriber) => {
// ...
});
});
```ts
import { Observable, merge } from 'rxjs';
```
return merge(...observables);
```
```ts
const observables = options.targets.map((target) => {
return new Observable<DataQueryResponse>((subscriber) => {
// ...
});
});
1. In the `subscribe` function, create a `CircularDataFrame`.
return merge(...observables);
```
```ts
import { CircularDataFrame } from '@grafana/data';
```
### Step 4: Create a `CircularDataFrame` instance
```ts
const frame = new CircularDataFrame({
append: 'tail',
capacity: 1000,
});
In the `subscribe` function, create a `CircularDataFrame` instance.
frame.refId = query.refId;
frame.addField({ name: 'time', type: FieldType.time });
frame.addField({ name: 'value', type: FieldType.number });
```
```ts
import { CircularDataFrame } from '@grafana/data';
```
Circular data frames have a limited capacity. When a circular data frame reaches its capacity, the oldest data point is removed.
```ts
const frame = new CircularDataFrame({
append: 'tail',
capacity: 1000,
});
1. Use `subscriber.next()` to send the updated data frame whenever you receive new updates.
frame.refId = query.refId;
frame.addField({ name: 'time', type: FieldType.time });
frame.addField({ name: 'value', type: FieldType.number });
```
```ts
import { LoadingState } from '@grafana/data';
```
Circular data frames have a limited capacity. When a circular data frame reaches its capacity, the oldest data point is removed.
```ts
const intervalId = setInterval(() => {
frame.add({ time: Date.now(), value: Math.random() });
### Step 5: Send the updated data frame
subscriber.next({
data: [frame],
key: query.refId,
state: LoadingState.Streaming,
});
}, 500);
Use `subscriber.next()` to send the updated data frame whenever you receive new updates.
return () => {
clearInterval(intervalId);
};
```
```ts
import { LoadingState } from '@grafana/data';
```
> **Note:** In practice, you'd call `subscriber.next` as soon as you receive new data from a websocket or a message bus. The example above simulates data being received every 500 milliseconds.
```ts
const intervalId = setInterval(() => {
frame.add({ time: Date.now(), value: Math.random() });
Here's the final `query` method.
subscriber.next({
data: [frame],
key: query.refId,
state: LoadingState.Streaming,
});
}, 500);
return () => {
clearInterval(intervalId);
};
```
> **Note:** In practice, you'd call `subscriber.next` as soon as you receive new data from a websocket or a message bus. In the example above, data is being received every 500 milliseconds.
### Example code for final `query` method
```ts
query(options: DataQueryRequest<MyQuery>): Observable<DataQueryResponse> {
@ -134,4 +146,6 @@ query(options: DataQueryRequest<MyQuery>): Observable<DataQueryResponse> {
}
```
One limitation with this example is that the panel visualization is cleared every time you update the dashboard. If you have access to historical data, you can add, or _backfill_, it to the data frame before the first call to `subscriber.next()`.
One limitation with this example is that the panel visualization is cleared every time you update the dashboard. If you have access to historical data, you can add it, or _backfill_ it, to the data frame before the first call to `subscriber.next()`.
For another example of a streaming plugin, refer to the [streaming websocket example](https://github.com/grafana/grafana-plugin-examples/tree/main/examples/datasource-streaming-websocket) on GitHub.

View File

@ -0,0 +1,27 @@
---
description: How-to topics for plugin development
title: Create a plugin
---
# Create a Grafana plugin
This section contains how-to topics for developing Grafana plugins.
- [Build a Grafana plugin](https://grafana.github.io/plugin-tools/docs/creating-a-plugin)
- [Build a panel plugin](https://grafana.com/tutorials/build-a-panel-plugin/)
- [Build a data source plugin](https://grafana.com/tutorials/build-a-data-source-plugin/)
- [Build a data source backend plugin](https://grafana.com/tutorials/build-a-data-source-backend-plugin/)
- [Build a logs data source plugin]({{< relref "../build-a-logs-data-source-plugin.md">}})
- [Build a streaming data source plugin]({{< relref "../build-a-streaming-data-source-plugin.md">}})
- Extend a Grafana plugin
- [Add annotations]({{< relref "add-support-for-annotations.md">}})
- [Add anonymous usage reporting]({{< relref "add-anonymous-usage-reporting.md">}})
- [Add authentication for a data source plugin]({{< relref "add-authentication-for-data-source-plugins.md">}})
- [Add Explore queries]({{< relref "add-support-for-explore-queries.md">}})
- [Add query editor help]({{< relref "add-query-editor-help.md">}})
- [Add variables]({{< relref "add-support-for-variables.md">}})
- [Create panel option editors]({{< relref "custom-panel-option-editors.md">}})
- [Sign a plugin]({{< relref "sign-a-plugin.md">}})
- [Automate development with CI](https://grafana.github.io/plugin-tools/docs/ci)
- [Create nested plugins](https://grafana.github.io/plugin-tools/docs/nested-plugins)
- [Extend configurations](https://grafana.github.io/plugin-tools/docs/advanced-configuration)

View File

@ -6,7 +6,7 @@ title: Data frames
Grafana supports a variety of different data sources, each with its own data model. To make this possible, Grafana consolidates the query results from each of these data sources into one unified data structure called a _data frame_.
The data frame structure is a concept that's borrowed from data analysis tools like the [R programming language](https://www.r-project.org), and [Pandas](https://pandas.pydata.org/).
The data frame structure is a concept that's borrowed from data analysis tools like the [R programming language](https://www.r-project.org) and [Pandas](https://pandas.pydata.org/).
> Data frames are available in Grafana 7.0+, and replaced the Time series and Table structures with a more generic data structure that can support a wider range of data types.
@ -161,7 +161,7 @@ Dimensions: 5 fields by 2 rows
+---------------------+------------------+------------------+------------------+------------------+
```
> **Note:** Not all panels support the wide time series data frame format. To keep full backward compatibility we have introduced a transformation that can be used to convert from the wide to the long format. For usage information, refer to the [Prepare time series-transformation]({{< relref "../../panels-visualizations/query-transform-data/transform-data/#prepare-time-series" >}}).
> **Note:** Not all panels support the wide time series data frame format. To keep full backward compatibility we've introduced a transformation that can be used to convert from the wide to the long format. For usage information, refer to the [Prepare time series-transformation]({{< relref "../../panels-visualizations/query-transform-data/transform-data/#prepare-time-series" >}}).
## Technical references

View File

@ -0,0 +1,12 @@
---
description: Get started guide
title: Get started with plugins
---
# Get started with Grafana plugins
This section contains guidance for building plugins.
- [Get started with creating a plugin](https://grafana.github.io/plugin-tools/docs/getting-started)
- [Types of Grafana plugins](https://grafana.com/docs/grafana/latest/administration/plugin-management/)
- [Set up your development environment](https://grafana.github.io/plugin-tools/docs/docker)

View File

@ -0,0 +1,13 @@
---
description: Conceptual topics for plugin development
title: Introduction to plugin development
---
# Introduction to Grafana plugin development
This section contains topics related to the key concepts for Grafana plugin development.
- [Backend plugins]({{< relref "../backend/" >}})
- [Grafana plugin SDK for Go]({{< relref "../backend/grafana-plugin-sdk-for-go.md" >}})
- [Plugin protocol]({{< relref "../backend/plugin-protocol.md" >}})
- [Data frames]({{< relref "data-frames.md">}})

View File

@ -34,7 +34,7 @@ title: Migration guides
})();
</script>
# Plugin migration guides
# Plugin migration guide
The following guides help you identify the steps required to update a plugin following changes between versions of Grafana.

View File

@ -1,162 +1,60 @@
---
aliases:
- share-a-plugin/
- package-a-plugin/
description: Learn how to package and share your plugin.
title: Publish a plugin
type: docs
---
# Publish a plugin
# Publish or update a plugin
You've just built your plugin; now you want to share it with the world.
You've just built your first plugin, and now you want to share it with the world.
In this guide, you'll learn how to package and share your plugin with others.
For Grafana to be able to load a plugin, it first needs to be built.
When you build a plugin from source, a `dist` directory is created that contains the production build, or _plugin assets_, for your plugin.
When the Grafana server starts, it recursively looks in the plugin directory for any directory that contains a `plugin.json` file and tries to load the plugin assets in the same directory.
There are three steps needed to package a plugin:
- Building the plugin
- Signing the plugin
- Packaging the plugin
1. Build the plugin
```
yarn install --pure-lockfile
yarn build
```
1. (Optional) If your data source plugin has a backend plugin, build it as well.
```
mage
```
make sure that all the binaries are executable and have a `0755` (`-rwxr-xr-x`) permission
1. [Sign the plugin]({{< relref "./sign-a-plugin" >}}).
1. Rename `dist` directory to match your plugin id and create a ZIP archive
```
mv dist/ myorg-simple-panel
zip myorg-simple-panel-1.0.0.zip myorg-simple-panel -r
```
1. (Optional) Verify your plugin is packaged correctly using [zipinfo](https://linux.die.net/man/1/zipinfo).
It should look like this:
```
zipinfo grafana-clickhouse-datasource-1.1.2.zip
Archive: grafana-clickhouse-datasource-1.1.2.zip
Zip file size: 34324077 bytes, number of entries: 22
drwxr-xr-x 0 bx stor 22-Mar-24 23:23 grafana-clickhouse-datasource/
-rw-r--r-- 1654 bX defN 22-Mar-24 23:23 grafana-clickhouse-datasource/CHANGELOG.md
-rw-r--r-- 11357 bX defN 22-Mar-24 23:23 grafana-clickhouse-datasource/LICENSE
-rw-r--r-- 2468 bX defN 22-Mar-24 23:23 grafana-clickhouse-datasource/MANIFEST.txt
-rw-r--r-- 8678 bX defN 22-Mar-24 23:23 grafana-clickhouse-datasource/README.md
drwxr-xr-x 0 bx stor 22-Mar-24 23:23 grafana-clickhouse-datasource/dashboards/
-rw-r--r-- 42973 bX defN 22-Mar-24 23:23 grafana-clickhouse-datasource/dashboards/cluster-analysis.json
-rw-r--r-- 56759 bX defN 22-Mar-24 23:23 grafana-clickhouse-datasource/dashboards/data-analysis.json
-rw-r--r-- 39406 bX defN 22-Mar-24 23:23 grafana-clickhouse-datasource/dashboards/query-analysis.json
-rwxr-xr-x 16469136 bX defN 22-Mar-24 23:23 grafana-clickhouse-datasource/gpx_clickhouse_darwin_amd64
-rwxr-xr-x 16397666 bX defN 22-Mar-24 23:23 grafana-clickhouse-datasource/gpx_clickhouse_darwin_arm64
-rwxr-xr-x 14942208 bX defN 22-Mar-24 23:23 grafana-clickhouse-datasource/gpx_clickhouse_linux_amd64
-rwxr-xr-x 14155776 bX defN 22-Mar-24 23:23 grafana-clickhouse-datasource/gpx_clickhouse_linux_arm
-rwxr-xr-x 14548992 bX defN 22-Mar-24 23:23 grafana-clickhouse-datasource/gpx_clickhouse_linux_arm64
-rwxr-xr-x 15209472 bX defN 22-Mar-24 23:23 grafana-clickhouse-datasource/gpx_clickhouse_windows_amd64.exe
drwxr-xr-x 0 bx stor 22-Mar-24 23:23 grafana-clickhouse-datasource/img/
-rw-r--r-- 304 bX defN 22-Mar-24 23:23 grafana-clickhouse-datasource/img/logo.png
-rw-r--r-- 1587 bX defN 22-Mar-24 23:23 grafana-clickhouse-datasource/img/logo.svg
-rw-r--r-- 138400 bX defN 22-Mar-24 23:23 grafana-clickhouse-datasource/module.js
-rw-r--r-- 808 bX defN 22-Mar-24 23:23 grafana-clickhouse-datasource/module.js.LICENSE.txt
-rw-r--r-- 487395 bX defN 22-Mar-24 23:23 grafana-clickhouse-datasource/module.js.map
-rw-r--r-- 1616 bX defN 22-Mar-24 23:23 grafana-clickhouse-datasource/plugin.json
22 files, 92516655 bytes uncompressed, 34319591 bytes compressed: 62.9%
```
## Publish your plugin on Grafana.com
The best way to share your plugin with the world is to publish it on [Grafana Plugins](/plugins).
The best way to share your plugin with the world is to publish it in the [Grafana plugin catalog]{{< relref "/plugins" >}}).
By having your plugin published on Grafana.com, more users will be able to discover your plugin.
Before you submit your plugin, we ask that you read our guidelines and frequently asked questions.
## Before you begin
### Guidelines
When you build a plugin from source, a `dist` folder is created. This folder contains the production build or _plugin assets_ for your plugin.
To package a plugin, refer to [Package a plugin]{{< relref "package-a-plugin.md" >}}).
### Follow our guidelines
Get familiar with our plugin [publishing and signing criteria]({{< relref "./publishing-and-signing-criteria" >}})
### Do this for best results
To speed up the time it takes to review your plugin:
- Get familiar with our plugin [publishing and signing criteria]({{< relref "./publishing-and-signing-criteria" >}})
- Check that your plugin is ready for review using the [plugin validator](https://github.com/grafana/plugin-validator).
- Read our [6 tips for improving your Grafana plugin before you publish](/blog/2021/01/21/6-tips-for-improving-your-grafana-plugin-before-you-publish/).
- Refer to [plugin-examples](https://github.com/grafana/grafana-plugin-examples) to review best practices for building your plugin.
### Frequently Asked Questions
## Publish your plugin
**Do I need to submit a private plugin?**
- No. Please only submit plugins that you wish to make publicly available for the Grafana community.
**How long does it take to review my submission?**
- We're not able to give an estimate at this time, though we're constantly working on improving the time it takes to review a plugin.
**Can I decide a date when my plugin will be published?**
- No. We cannot guarantee specific publishing dates, as plugins are immediately published after a review based on our internal prioritization.
**Can I see metrics of my plugin installs, downloads or usage?**
- No. We don't offer this information at the moment to plugin authors.
**How can I update my plugin's catalog page?**
- The plugin's catalog page content is extracted from the plugin README file.
To update the plugin's catalog page, it is necessary to submit an updated plugin with the new content included in the README file.
**Can I unlist my plugin from the Grafana Plugin's Catalog in case of a bug?**
- In the event of a bug, unlisting the plugin from the Grafana Plugin's Catalog may be possible in exceptional cases, such as security concerns.
However, we do not have control over the specific instances where the plugin is installed.
**Can I distribute my plugin somewhere else than the Grafana Catalog?**
- The official method for distributing Grafana plugins is through the Grafana Catalog.
Alternative methods, such as installing private or development plugins on local Grafana instances, are available as per the guidelines provided in [this guide]({{< relref "../../administration/plugin-management#install-plugin-on-local-grafana" >}}).
**Can I still use Angular for my plugin?**
- No. We will not accept any new plugin submissions written in angular.
Please take a look at our [angular support deprecation documentation]({{< relref "../angular_deprecation" >}}).
### Publishing your plugin for the first time
**Do plugin signatures expire?**
- Plugin signatures do not currently expire.
{{< figure src="/static/img/docs/plugins/plugins-submission-create2.png" class="docs-image--no-shadow" max-width="650px" >}}
Follow these steps to publish your plugin for the first time.
1. [Sign in](/auth/sign-in) to your Grafana Cloud account.
1. In the left menu, under **Org settings**, click **My Plugins**.
1. Click **Submit Plugin**.
1. Click **Submit Plugin**. The Create Plugin Submission form appears.
{{< figure src="/static/img/docs/plugins/plugins-submission-create2.png" class="docs-image--no-shadow" max-width="650px" >}}
1. Enter the information requested by the form.
- **OS & Architecture:**
- Select **Single** if your plugin archive contains binaries for multiple architectures.
- Select **Multiple** if you'd like to submit separate plugin archives for each architecture.
This can lead to faster downloads since users can select the specific architecture on which they want to install the plugin.
- **URL:** A URL that points to a ZIP archive of your packaged plugin.
- **Source Code URL:** A URL that points to a public git repository or ZIP archive of your complete plugin source code.
- **Source Code URL:** A URL that points to a public Git repository or ZIP archive of your complete plugin source code.
- **MD5:** The MD5 hash of the plugin specified by the **URL**.
- The remaining questions help us determine the [signature level]({{< relref "./sign-a-plugin#plugin-signature-levels" >}}) for your plugin.
1. Click **Submit**.
After you have submitted your plugin, we run an automated validation to make sure it adheres to our guidelines.
Once your submission passes the validation, it's placed in a review queue.
After you submit your plugin, we run an automated validation to make sure it adheres to our guidelines.
Your submission is placed in the review queue after the submission is validated.
All submissions are manually inspected by a plugin reviewer.
For every new plugin, we perform a manual review that includes the following checks:
@ -165,21 +63,69 @@ For every new plugin, we perform a manual review that includes the following che
If you're unable to make the source code publicly available, let us know in a comment on your plugin submission.
- **Tests:** We install your plugin on one of our Grafana instances to test it for basic use.
For more advanced plugins, we may ask you to assist us in configuring a test environment for the plugin.
This test environment will be used whenever you submit an plugin update.
We use the test environment whenever you submit a plugin update.
### Submit a plugin update
## Update your plugin
To submit an **update** for an already published plugin:
1. [Sign in](/auth/sign-in) to your Grafana Cloud account.
1. In the left menu, under **Org settings**, click **My Plugins**.
1. Click **Submit Update** for the plugin you want to update.
1. Click **Submit Update** for the plugin you want to update. The Create Plugin Submission form appears.
{{< figure src="/static/img/docs/plugins/plugins-submission-create2.png" class="docs-image--no-shadow" max-width="650px" >}}
1. Enter the information requested by the form.
- **OS & Architecture:**
- Select **Single** if your plugin archive contains binaries for multiple architectures.
- Select **Multiple** if you'd like to submit separate plugin archives for each architecture.
This can lead to faster downloads since users can select the specific architecture they want to install the plugin on.
- **URL:** A URL that points to a ZIP archive of your packaged plugin.
- **Source Code URL:** A URL that points to a public git repository or ZIP archive of your complete plugin source code.
- **Source Code URL:** A URL that points to a public Git repository or ZIP archive of your complete plugin source code. See [examples](#what-source-code-url-formats-are-supported).
- **MD5:** The MD5 hash of the plugin specified by the **URL**.
1. Click **Submit**.
## Frequently asked questions
### Do I need to submit a private plugin?
- No. Please only submit plugins that you wish to make publicly available for the Grafana community.
### How long does it take to review my submission?
- We're not able to give an estimate at this time, though we're constantly working on improving the time it takes to review a plugin.
### Can I decide a date when my plugin will be published?
- No. We cannot guarantee specific publishing dates, as plugins are immediately published after a review based on our internal prioritization.
### Can I see metrics of my plugin installs, downloads or usage?
- No. We don't offer this information at the moment to plugin authors.
### How can I update my plugin's catalog page?
- The plugin's catalog page content is extracted from the plugin README file.
To update the plugin's catalog page, submit an updated plugin with the new content included in the README file.
### Can I unlist my plugin from the Grafana plugin catalog in case of a bug?
- In the event of a bug, unlisting the plugin from our catalog may be possible in exceptional cases, such as security concerns. However, we don't have control over the specific instances where the plugin is installed.
### Can I distribute my plugin somewhere else other than the Grafana plugin catalog?
- The official method for distributing Grafana plugins is through our catalog. Alternative methods, such as installing private or development plugins on local Grafana instances, are available as per the guidelines provided in [this guide]({{< relref "../../administration/plugin-management#install-plugin-on-local-grafana" >}}).
### Can I still use Angular for my plugin?
- No. We will not accept any new plugin submissions written in Angular. For more information, refer to our [Angular support deprecation documentation]({{< relref "../angular_deprecation" >}}).
### Do plugin signatures expire?
- Plugin signatures do not currently expire.
### What source code URL formats are supported?
- Using a tag or branch: `https://github.com/grafana/clock-panel/tree/v2.1.3`
- A tag or branch and the code is in a subdirectory (important for mono repos): `https://github.com/grafana/clock-panel/tree/v2.1.3/plugin/` (here, the plugin contains the plugin code)
- Using the latest main or master branch commit: `https://github.com/grafana/clock-panel/` (not recommended, it's better to pass a tag or branch)

View File

@ -0,0 +1,11 @@
---
description: Topics related to plugin publishing
title: Publish a plugin
---
# Publish a Grafana plugin
This section contains topics related to publishing Grafana plugins.
- [Publishing and signing criteria]({{< relref "publishing-and-signing-criteria.md">}})
- [Publish a plugin]({{< relref "publish-a-plugin.md">}})

View File

@ -4,28 +4,13 @@ title: Plugin publishing and signing criteria
# Plugin publishing and signing criteria
Grafana plugins must adhere to the following criteria when being reviewed for publishing and signing.
Grafana plugins must adhere to the Grafana Labs [Plugin Policy](https://grafana.com/legal/plugins/). Our review process for publishing and signing will examine your compliance with this policy.
## Privacy and security
- Plugins cannot collect usage or user information. Violations of this include but is not limited to:
- Directly collecting installation and user statistics
- Sending data to 3rd parties for analytics purposes
- Embedding tracking code
- Data at rest: sensitive data such as credentials and user information, must be encrypted using industry standards.
- Use secureJsonData to store data source credentials
- Secrets cannot be stored in panel options
- Data transmission: secure methods that meet industry standard encryption levels should be used, such as Secure Sockets Layer (SSL) or Transport Layer Security (TLS)
- Abuse: plugins should not perform actions beyond the scope of the intended use.
- Do not include hidden files
- Do not manipulate the underlying environment, privileges, or related processes
- Security:
- Should not access the filesystem
- Should not access environment variables
> **Important:** Grafana Labs reserves the right to decline or remove any plugin at its discretion. Failure to comply with publishing and signing criteria may result in immediate removal from the Grafana plugin catalog.
## Plugin licensing
Plugins must be licensed under one of the following AGPL compliant licenses for publishing to the catalog:
Plugins must be licensed under one of the following AGPL compliant licenses for publishing to the Grafana plugin catalog:
- AGPL-3.0
- Apache-2.0
@ -35,9 +20,3 @@ Plugins must be licensed under one of the following AGPL compliant licenses for
- MIT
If contributing a plugin on behalf of an organization, be sure to seek guidance from your legal team.
## Commercial
- Usage of 3rd party software or dependencies within the plugin must be licensed for the intended use. For example, using open source dependencies must be credited/licensed; embedding logos or trademarks;
Grafana Labs reserves the right to decline or remove any plugin at its discretion. Failure to comply with publishing and signing criteria may result in immediate removal from the Grafana plugin catalog.

View File

@ -0,0 +1,10 @@
---
description: Reference topics.
title: Reference
---
# Reference
This section contains a metadata reference for plugin development.
- [{{< relref "plugin.json">}}](metadata.md)

View File

@ -4,17 +4,19 @@ title: Sign a plugin
# Sign a plugin
Signing a plugin allows Grafana to verify the authenticity of the plugin with [signature verification]({{< relref "../../administration/plugin-management#plugin-signatures" >}}). This gives users a way to make sure plugins haven't been tampered with. By [default]({{< relref "../../administration/plugin-management#allow-unsigned-plugins" >}}), Grafana **requires** all plugins to be signed in order for them to be loaded. All Grafana Labs-authored backend plugins, including Enterprise plugins, are signed.
Grafana requires all plugins to be signed so that we can verify their authenticity with [signature verification]({{< relref "../../administration/plugin-management#plugin-signatures" >}}).
All Grafana Labs-authored backend plugins, including Enterprise plugins, are signed. By [default]({{< relref "../../administration/plugin-management#allow-unsigned-plugins" >}}), Grafana **requires** all plugins to be signed in order for them to be loaded.
Before you can sign your plugin, you need to decide whether you want to sign it as a _public_ or a _private_ plugin.
If you want to make your plugin publicly available outside of your organization, you need to sign your plugin under a _community_ or _commercial_ [signature level](#plugin-signature-levels). Public plugins are available from [grafana.com/plugins](https://grafana.com/plugins) and can be installed by anyone.
To make your plugin publicly available outside of your organization, sign your plugin under a _community_ or _commercial_ [signature level](#plugin-signature-levels). Public plugins are available from the [Grafana plugin catalog](https://grafana.com/plugins) and can be installed by anyone.
If you intend to only use the plugin within your organization, you can sign it under a _private_ [signature level](#plugin-signature-levels).
If you intend to only use the plugin within your organization, sign it under a _private_ [signature level](#plugin-signature-levels).
## Generate an API key
To verify ownership of your plugin, you need to generate an API key that you'll use every time you need to sign a new version of your plugin.
To verify ownership of your plugin, generate an API key that you'll use every time you need to sign a new version of your plugin.
1. [Create a Grafana Cloud account](https://grafana.com/signup).
@ -29,8 +31,8 @@ To verify ownership of your plugin, you need to generate an API key that you'll
Public plugins need to be reviewed by the Grafana team before you can sign them.
1. Submit your plugin for [review]({{< relref "publish-a-plugin/#publishing-your-plugin-for-the-first-time" >}})
2. When your plugin is approved, you're granted a plugin signature level. **Without a plugin signature level, you won't be able to sign your plugin**.
3. In your plugin directory, sign the plugin with the API key you just created. Grafana Sign Plugin creates a [MANIFEST.txt](#plugin-manifest) file in the `dist` directory of your plugin.
1. If we approve your plugin, you're granted a plugin signature level. You need this signature level to proceed.
1. In your plugin directory, sign the plugin with the API key you just created. Grafana Sign Plugin creates a [MANIFEST.txt](#plugin-manifest) file in the `dist` directory of your plugin:
```bash
export GRAFANA_API_KEY=<YOUR_API_KEY>
@ -41,24 +43,24 @@ Public plugins need to be reviewed by the Grafana team before you can sign them.
1. In your plugin directory, sign the plugin with the API key you just created. Grafana Sign Plugin creates a [MANIFEST.txt](#plugin-manifest) file in the `dist` directory of your plugin.
The `rootUrls` flag accepts a comma-separated list of URLs to the Grafana instances where you intend to install the plugin.
```bash
export GRAFANA_API_KEY=<YOUR_API_KEY>
npx @grafana/sign-plugin@latest --rootUrls https://example.com/grafana
```
1. After the `rootUrls` flag, enter a comma-separated list of URLs for the Grafana instances where you intend to install the plugin.
## Plugin signature levels
To sign a plugin, you need to decide the _signature level_ you want to sign it under. The signature level of your plugin determines how you can distribute it.
To sign a plugin, you need to select the _signature level_ that you want to sign it under. The signature level of your plugin determines how you can distribute it.
You can sign your plugin under three different _signature levels_.
You can sign your plugin under three different _signature levels_: _private_, _community_, and _commercial_.
| **Plugin Level** | **Paid Subscription Required?** | **Description** |
| ---------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Private | No;<br>Free of charge | <p>You can create and sign a Private Plugin for any technology at no charge.</p><p>Private Plugins are for use on your own Grafana. They may not be distributed to the Grafana community, and are not published in the Grafana catalog.</p> |
| Community | No;<br>Free of charge | <p>You can create, sign and distribute plugins at no charge, provided that all dependent technologies are open source and not for profit.</p><p>Community Plugins are published in the official Grafana catalog, and are available to the Grafana community.</p> |
| Commercial | Yes;<br>Commercial Plugin Subscription required | <p>You can create, sign and distribute plugins with dependent technologies that are closed source or commercially backed, by entering into a Commercial Plugin Subscription with Grafana Labs.</p><p>Commercial Plugins are published on the official Grafana catalog, and are available to the Grafana community.</p> |
| **Plugin Level** | **Paid Subscription Required?** | **Description** |
| ---------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Private | No;<br>Free of charge | <p>You can create and sign a Private plugin for any technology at no charge.</p><p>Private plugins are intended for use on your own installation of Grafana. They may not be distributed to the Grafana community, and they are not published in the Grafana plugin catalog.</p> |
| Community | No;<br>Free of charge | <p>You can create, sign, and distribute plugins at no charge, provided that all dependent technologies are open source and not for profit.</p><p>Community plugins are published in the official Grafana catalog, and are available to the entire Grafana community.</p> |
| Commercial | Yes;<br>Commercial plugin subscription required | <p>You can create, sign, and distribute plugins with dependent technologies that are closed source or commercially backed. To do so, enter into a Commercial plugin subscription with Grafana Labs.</p><p>Commercial plugins are published on the Grafana plugin catalog, and are available to the entire Grafana community.</p> |
For instructions on how to sign a plugin under the Community and Commercial signature level, refer to [Sign a public plugin](#sign-a-public-plugin).
@ -66,12 +68,12 @@ For instructions on how to sign a plugin under the Private signature level, refe
## Plugin manifest
For Grafana to verify the digital signature of a plugin, the plugin must include a signed manifest file, _MANIFEST.txt_. The signed manifest file contains two sections:
For Grafana to verify the digital signature of a plugin, the plugin must include a signed manifest file, `MANIFEST.txt`. The signed manifest file contains two sections:
- **Signed message -** The signed message contains plugin metadata and plugin files with their respective checksums (SHA256).
- **Digital signature -** The digital signature is created by encrypting the signed message using a private key. Grafana has a public key built-in that can be used to verify that the digital signature have been encrypted using expected private key.
- **Signed message -** Contains plugin metadata and plugin files with their respective checksums (SHA256).
- **Digital signature -** Created by encrypting the signed message using a private key. Grafana has a public key built-in that can be used to verify that the digital signature has been encrypted using the expected private key.
**Example manifest file:**
**Example**
```txt
-----BEGIN PGP SIGNED MESSAGE-----
@ -107,16 +109,18 @@ T6scfmuhWC/TOcm83EVoCzIV3R5dOTKHqkjIUg==
-----END PGP SIGNATURE-----
```
## Troubleshooting issues while signing your plugin
## Troubleshooting
### Why am I getting a "Modified signature" in Grafana?
### Why do I get a "Modified signature" error?
Due to an issue when signing the plugin on Windows, in some cases an invalid MANIFEST.txt is being generated. You can fix this by replacing all double backslashes, `\\`, with a forward slash, `/` in the MANIFEST.txt file. You need to do this every time you sign your plugin.
In some cases an invalid `MANIFEST.txt` is generated because of an issue when signing the plugin on Windows. You can fix this by replacing all double backslashes, `\\`, with a forward slash, `/`, in the `MANIFEST.txt` file. You need to do this every time you sign your plugin.
### Error signing manifest: Field is required: rootUrls
### Why do I get a "Field is required: `rootUrls`" error for my public plugin?
If you're trying to sign a **public** plugin, this means that your plugin doesn't have a plugin signature level assigned to it yet. A Grafana team member will assign a signature level to your plugin once it has been reviewed and approved. For more information, refer to [Sign a public plugin](#sign-a-public-plugin).
With a **public** plugin, your plugin doesn't have a plugin signature level assigned to it yet. A Grafana team member will assign a signature level to your plugin once it has been reviewed and approved. For more information, refer to [Sign a public plugin](#sign-a-public-plugin).
If you're trying to sign a **private** plugin, this means that you need to add a `rootUrls` flag to the `plugin:sign` command. The `rootUrls` must match the [root_url]({{< relref "../../setup-grafana/configure-grafana/#root_url" >}}) configuration. For more information, refer to [Sign a private plugin](#sign-a-private-plugin).
### Why do I get a "Field is required: `rootUrls`" error for my private plugin?
With a **private** plugin, you need to add a `rootUrls` flag to the `plugin:sign` command. The `rootUrls` must match the [root_url]({{< relref "../../setup-grafana/configure-grafana/#root_url" >}}) configuration. For more information, refer to [Sign a private plugin](#sign-a-private-plugin).
If you still get this error, make sure that the API key was generated by a Grafana Cloud account that matches the first part of the plugin ID.