Stackdriver: Rename Stackdriver to Google Cloud Monitoring (#25807)

* Update backend

* Update frontend

* Keep old plugin id

* Update docs

* Place doc images to a new directory

* Legacy support for stackdriver-auto alignment

* Consistent plugin name

* Apply suggestions from code review

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
Co-authored-by: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com>

* Update docs

* Update public/app/plugins/datasource/cloud-monitoring/README.md

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>

* Add reference to the data source formerly being named Stackdriver

* Update pkg/models/datasource.go

Co-authored-by: Carl Bergquist <carl@grafana.com>

* Fix gofmt

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
Co-authored-by: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com>
Co-authored-by: Carl Bergquist <carl@grafana.com>
This commit is contained in:
Sofia Papagiannaki 2020-06-30 18:47:13 +03:00 committed by GitHub
parent 05bfd17b00
commit 4bb3f66569
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
68 changed files with 340 additions and 268 deletions

2
.github/CODEOWNERS vendored
View File

@ -56,5 +56,5 @@ lerna.json @grafana/grafana-frontend-platform
/public/app/plugins/datasource/opentsdb @grafana/backend-platform
/public/app/plugins/datasource/postgres @grafana/backend-platform
/public/app/plugins/datasource/prometheus @grafana/observability-squad
/public/app/plugins/datasource/stackdriver @grafana/backend-platform
/public/app/plugins/datasource/cloud-monitoring @grafana/backend-platform
/public/app/plugins/datasource/zipkin @grafana/observability-squad

View File

@ -91,9 +91,9 @@ aliases = ["/docs/grafana/v1.1", "/docs/grafana/latest/guides/reference/admin",
<img src="/img/docs/logos/icon_prometheus.svg" >
<h5>Prometheus</h5>
</a>
<a href="{{< relref "features/datasources/stackdriver.md" >}}" class="nav-cards__item nav-cards__item--ds">
<img src="/img/docs/logos/icon_stackdriver.svg">
<h5>Google Stackdriver</h5>
<a href="{{< relref "features/datasources/cloudmonitoring.md" >}}" class="nav-cards__item nav-cards__item--ds">
<img src="/img/docs/logos/icon_cloudmonitoring.svg">
<h5>Google Cloud Monitoring</h5>
</a>
<a href="{{< relref "features/datasources/cloudwatch.md" >}}" class="nav-cards__item nav-cards__item--ds">
<img src="/img/docs/logos/icon_cloudwatch.svg">

View File

@ -41,7 +41,7 @@ The actual notifications are configured and shared between multiple alerts.
Alert rules are evaluated in the Grafana backend in a scheduler and query execution engine that is part
of core Grafana. Only some data sources are supported right now. They include `Graphite`, `Prometheus`, `InfluxDB`, `Elasticsearch`,
`Stackdriver`, `Cloudwatch`, `Azure Monitor`, `MySQL`, `PostgreSQL`, `MSSQL`, `OpenTSDB`, `Oracle`, and `Azure Data Explorer`.
`Google Cloud Monitoring`, `Cloudwatch`, `Azure Monitor`, `MySQL`, `PostgreSQL`, `MSSQL`, `OpenTSDB`, `Oracle`, and `Azure Data Explorer`.
## Metrics from the alert engine

View File

@ -42,7 +42,7 @@ The actual notifications are configured and shared between multiple alerts.
Alert rules are evaluated in the Grafana backend in a scheduler and query execution engine that is part
of core Grafana. Only some data sources are supported right now. They include `Graphite`, `Prometheus`, `InfluxDB`, `Elasticsearch`,
`Stackdriver`, `Cloudwatch`, `Azure Monitor`, `MySQL`, `PostgreSQL`, `MSSQL`, `OpenTSDB`, `Oracle`, and `Azure Data Explorer`.
`Google Cloud Monitoring`, `Cloudwatch`, `Azure Monitor`, `MySQL`, `PostgreSQL`, `MSSQL`, `OpenTSDB`, `Oracle`, and `Azure Data Explorer`.
## Metrics from the alert engine

View File

@ -24,7 +24,7 @@ The following data sources are officially supported:
* [AWS CloudWatch]({{< relref "cloudwatch.md" >}})
* [Azure Monitor]({{< relref "azuremonitor.md" >}})
* [Elasticsearch]({{< relref "elasticsearch.md" >}})
* [Google Stackdriver]({{< relref "stackdriver.md" >}})
* [Google Cloud Monitoring]({{< relref "cloudmonitoring.md" >}})
* [Graphite]({{< relref "graphite.md" >}})
* [InfluxDB]({{< relref "influxdb.md" >}})
* [Loki]({{< relref "loki.md" >}})

View File

@ -1,28 +1,29 @@
+++
title = "Using Stackdriver in Grafana"
description = "Guide for using Stackdriver in Grafana"
keywords = ["grafana", "stackdriver", "google", "guide"]
title = "Using Google Cloud Monitoring in Grafana"
description = "Guide for using Google Cloud Monitoring in Grafana"
keywords = ["grafana", "stackdriver", "google", "guide", "cloud", "monitoring"]
type = "docs"
aliases = ["/docs/grafana/latest/datasources/stackdriver"]
aliases = ["/docs/grafana/latest/datasources/stackdriver", "/docs/grafana/latest/features/datasources/stackdriver/"]
[menu.docs]
name = "Google Stackdriver"
name = "Google Cloud Monitoring"
parent = "datasources"
weight = 4
+++
# Using Google Stackdriver in Grafana
# Using Google Cloud Monitoring in Grafana
> Available as a beta feature in Grafana v5.3.x and v5.4.x.
> Officially released in Grafana v6.0.0
Grafana ships with built-in support for Google Stackdriver. Just add it as a data source and you are ready to build dashboards for your Stackdriver metrics.
> Before Grafana v7.1 this data source was named Google Stackdriver.
Grafana ships with built-in support for Google Cloud Monitoring. Just add it as a data source and you are ready to build dashboards for your Google Cloud Monitoring metrics.
## Adding the data source
1. Open the side menu by clicking the Grafana icon in the top header.
2. In the side menu under the `Dashboards` link you should find a link named `Data Sources`.
3. Click the `+ Add data source` button in the top header.
4. Select `Stackdriver` from the _Type_ dropdown.
4. Select `Google Cloud Monitoring` from the _Type_ dropdown.
5. Upload or paste in the Service Account Key file. See below for steps on how to create a Service Account Key file.
> NOTE: If you're not seeing the `Data Sources` link in your side menu it means that your current user does not have the `Admin` role for the current organization.
@ -35,11 +36,11 @@ Grafana ships with built-in support for Google Stackdriver. Just add it as a dat
## Authentication
There are two ways to authenticate the Stackdriver plugin - either by uploading a Google JWT file, or by automatically retrieving credentials from Google metadata server. The latter option is only available when running Grafana on GCE virtual machine.
There are two ways to authenticate the Google Cloud Monitoring plugin - either by uploading a Google JWT file, or by automatically retrieving credentials from Google metadata server. The latter option is only available when running Grafana on GCE virtual machine.
### Using a Google Service Account Key File
To authenticate with the Stackdriver API, you need to create a Google Cloud Platform (GCP) Service Account for the Project you want to show data for. A Grafana data source integrates with one GCP Project. If you want to visualize data from multiple GCP Projects then you need to create one data source per GCP Project.
To authenticate with the Google Cloud Monitoring API, you need to create a Google Cloud Platform (GCP) Service Account for the Project you want to show data for. A Grafana data source integrates with one GCP Project. If you want to visualize data from multiple GCP Projects then you need to create one data source per GCP Project.
#### Enable APIs
@ -50,31 +51,31 @@ The following APIs need to be enabled first:
Click on the links above and click the `Enable` button:
{{< docs-imagebox img="/img/docs/v53/stackdriver_enable_api.png" class="docs-image--no-shadow" caption="Enable GCP APIs" >}}
{{< docs-imagebox img="/img/docs/v71/cloudmonitoring_enable_api.png" class="docs-image--no-shadow" caption="Enable GCP APIs" >}}
#### Create a GCP Service Account for a Project
1. Navigate to the [APIs and Services Credentials page](https://console.cloud.google.com/apis/credentials).
2. Click on the `Create credentials` dropdown/button and choose the `Service account key` option.
{{< docs-imagebox img="/img/docs/v53/stackdriver_create_service_account_button.png" class="docs-image--no-shadow" caption="Create service account button" >}}
{{< docs-imagebox img="/img/docs/v71/cloudmonitoring_create_service_account_button.png" class="docs-image--no-shadow" caption="Create service account button" >}}
3. On the `Create service account key` page, choose key type `JSON`. Then in the `Service Account` dropdown, choose the `New service account` option:
{{< docs-imagebox img="/img/docs/v53/stackdriver_create_service_account_key.png" class="docs-image--no-shadow" caption="Create service account key" >}}
{{< docs-imagebox img="/img/docs/v71/cloudmonitoring_create_service_account_key.png" class="docs-image--no-shadow" caption="Create service account key" >}}
4. Some new fields will appear. Fill in a name for the service account in the `Service account name` field and then choose the `Monitoring Viewer` role from the `Role` dropdown:
{{< docs-imagebox img="/img/docs/v53/stackdriver_service_account_choose_role.png" class="docs-image--no-shadow" caption="Choose role" >}}
{{< docs-imagebox img="/img/docs/v71/cloudmonitoring_service_account_choose_role.png" class="docs-image--no-shadow" caption="Choose role" >}}
5. Click the Create button. A JSON key file will be created and downloaded to your computer. Store this file in a secure place as it allows access to your Stackdriver data.
5. Click the Create button. A JSON key file will be created and downloaded to your computer. Store this file in a secure place as it allows access to your Google Cloud Monitoring data.
6. Upload it to Grafana on the data source Configuration page. You can either upload the file or paste in the contents of the file.
{{< docs-imagebox img="/img/docs/v53/stackdriver_grafana_upload_key.png" class="docs-image--no-shadow" caption="Upload service key file to Grafana" >}}
{{< docs-imagebox img="/img/docs/v71/cloudmonitoring_grafana_upload_key.png" class="docs-image--no-shadow" caption="Upload service key file to Grafana" >}}
7. The file contents will be encrypted and saved in the Grafana database. Don't forget to save after uploading the file!
{{< docs-imagebox img="/img/docs/v53/stackdriver_grafana_key_uploaded.png" class="docs-image--no-shadow" caption="Service key file is uploaded to Grafana" >}}
{{< docs-imagebox img="/img/docs/v71/cloudmonitoring_grafana_key_uploaded.png" class="docs-image--no-shadow" caption="Service key file is uploaded to Grafana" >}}
### Using GCE Default Service Account
@ -82,13 +83,13 @@ If Grafana is running on a Google Compute Engine (GCE) virtual machine, it is po
1. First of all, you need to create a Service Account that can be used by the GCE virtual machine. See detailed instructions on how to do that [here](https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances#createanewserviceaccount).
2. Make sure the GCE virtual machine instance is being run as the service account that you just created. See instructions [here](https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances#using).
3. Allow access to the `Stackdriver Monitoring API` scope. See instructions [here](changeserviceaccountandscopes).
3. Allow access to the `Cloud Monitoring Monitoring API` scope.
Read more about creating and enabling service accounts for GCE VM instances [here](https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances).
## Using the Query Editor
The Stackdriver query editor allows you to build two types of queries - **Metric** and **Service Level Objective (SLO)**. Both types return time series data.
The Google Cloud Monitoring query editor allows you to build two types of queries - **Metric** and **Service Level Objective (SLO)**. Both types return time series data.
### Metric Queries
@ -104,7 +105,7 @@ To create a metric query, follow these steps:
4. Choose a metric from the **Metric** dropdown.
5. Use the plus and minus icons in the filter and group by sections to add/remove filters or group by clauses. This step is optional.
Stackdriver metrics can be of different kinds (GAUGE, DELTA, CUMULATIVE) and these kinds have support for different aggregation options (reducers and aligners). The Grafana query editor shows the list of available aggregation methods for a selected metric and sets a default reducer and aligner when you select the metric. Units for the Y-axis are also automatically selected by the query editor.
Google Cloud Monitoring metrics can be of different kinds (GAUGE, DELTA, CUMULATIVE) and these kinds have support for different aggregation options (reducers and aligners). The Grafana query editor shows the list of available aggregation methods for a selected metric and sets a default reducer and aligner when you select the metric. Units for the Y-axis are also automatically selected by the query editor.
#### Filter
@ -126,14 +127,14 @@ The `Aligner` field allows you to align multiple time series after the same grou
##### Alignment Period/Group by Time
The `Alignment Period` groups a metric by time if an aggregation is chosen. The default is to use the GCP Stackdriver default groupings (which allows you to compare graphs in Grafana with graphs in the Stackdriver UI).
The option is called `Stackdriver auto` and the defaults are:
The `Alignment Period` groups a metric by time if an aggregation is chosen. The default is to use the GCP Google Cloud Monitoring default groupings (which allows you to compare graphs in Grafana with graphs in the Google Cloud Monitoring UI).
The option is called `cloud monitoring auto` and the defaults are:
- 1m for time ranges < 23 hours
- 5m for time ranges >= 23 hours and < 6 days
- 1h for time ranges >= 6 days
The other automatic option is `Grafana auto`. This will automatically set the group by time depending on the time range chosen and the width of the graph panel. Read more about the details [here](http://docs.grafana.org/variables/templates-and-variables/#the-interval-variable).
The other automatic option is `grafana auto`. This will automatically set the group by time depending on the time range chosen and the width of the graph panel. Read more about the details [here](http://docs.grafana.org/variables/templates-and-variables/#the-interval-variable).
It is also possible to choose fixed time intervals to group by, like `1h` or `1d`.
@ -190,7 +191,7 @@ Example Result: `gce_instance - compute.googleapis.com/instance/cpu/usage_time`
{{< docs-imagebox img="/img/docs/v70/slo-query-builder.png" max-width= "400px" class="docs-image--right" >}}
The SLO query builder in the Stackdriver data source allows you to display SLO data in time series format. To get an understanding of the basic concepts in service monitoring, please refer to Google Stackdriver's [official docs](https://cloud.google.com/monitoring/service-monitoring).
The SLO query builder in the Google Cloud Monitoring data source allows you to display SLO data in time series format. To get an understanding of the basic concepts in service monitoring, please refer to Google Cloud Monitoring's [official docs](https://cloud.google.com/monitoring/service-monitoring).
#### How to create an SLO query
@ -236,20 +237,20 @@ types of template variables.
### Query Variable
Variable of the type _Query_ allows you to query Stackdriver for various types of data. The Stackdriver data source plugin provides the following `Query Types`.
Variable of the type _Query_ allows you to query Google Cloud Monitoring for various types of data. The Google Cloud Monitoring data source plugin provides the following `Query Types`.
| Name | Description |
| -------------------------------- | ------------------------------------------------------------------------------------------------- |
| _Metric Types_ | Returns a list of metric type names that are available for the specified service. |
| _Labels Keys_ | Returns a list of keys for `metric label` and `resource label` in the specified metric. |
| _Labels Values_ | Returns a list of values for the label in the specified metric. |
| _Resource Types_ | Returns a list of resource types for the specified metric. |
| _Aggregations_ | Returns a list of aggregations (cross series reducers) for the specified metric. |
| _Aligners_ | Returns a list of aligners (per series aligners) for the specified metric. |
| _Alignment periods_ | Returns a list of all alignment periods that are available in Stackdriver query editor in Grafana |
| _Selectors_ | Returns a list of selectors that can be used in SLO (Service Level Objectives) queries |
| _SLO Services_ | Returns a list of Service Monitoring services that can be used in SLO queries |
| _Service Level Objectives (SLO)_ | Returns a list of SLO's for the specified SLO service |
| Name | Description |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| _Metric Types_ | Returns a list of metric type names that are available for the specified service. |
| _Labels Keys_ | Returns a list of keys for `metric label` and `resource label` in the specified metric. |
| _Labels Values_ | Returns a list of values for the label in the specified metric. |
| _Resource Types_ | Returns a list of resource types for the specified metric. |
| _Aggregations_ | Returns a list of aggregations (cross series reducers) for the specified metric. |
| _Aligners_ | Returns a list of aligners (per series aligners) for the specified metric. |
| _Alignment periods_ | Returns a list of all alignment periods that are available in Google Cloud Monitoring query editor in Grafana |
| _Selectors_ | Returns a list of selectors that can be used in SLO (Service Level Objectives) queries |
| _SLO Services_ | Returns a list of Service Monitoring services that can be used in SLO queries |
| _Service Level Objectives (SLO)_ | Returns a list of SLO's for the specified SLO service |
### Using variables in queries
@ -262,10 +263,10 @@ Why two ways? The first syntax is easier to read and write but does not allow yo
## Annotations
{{< docs-imagebox img="/img/docs/v53/stackdriver_annotations_query_editor.png" max-width= "400px" class="docs-image--right" >}}
{{< docs-imagebox img="/img/docs/v71/cloudmonitoring_annotations_query_editor.png" max-width= "400px" class="docs-image--right" >}}
[Annotations]({{< relref "../../reference/annotations.md" >}}) allow you to overlay rich event information on top of graphs. You add annotation
queries via the Dashboard menu / Annotations view. Annotation rendering is expensive so it is important to limit the number of rows returned. There is no support for showing Stackdriver annotations and events yet but it works well with [custom metrics](https://cloud.google.com/monitoring/custom-metrics/) in Stackdriver.
queries via the Dashboard menu / Annotations view. Annotation rendering is expensive so it is important to limit the number of rows returned. There is no support for showing Google Cloud Monitoring annotations and events yet but it works well with [custom metrics](https://cloud.google.com/monitoring/custom-metrics/) in Google Cloud Monitoring.
With the query editor for annotations, you can select a metric and filters. The `Title` and `Text` fields support templating and can use data returned from the query. For example, the Title field could have the following text:
@ -294,7 +295,7 @@ Here is a provisioning example using the JWT (Service Account key file) authenti
apiVersion: 1
datasources:
- name: Stackdriver
- name: Google Cloud Monitoring
type: stackdriver
access: proxy
jsonData:
@ -317,7 +318,7 @@ Here is a provisioning example using GCE Default Service Account authentication.
apiVersion: 1
datasources:
- name: Stackdriver
- name: Google Cloud Monitoring
type: stackdriver
access: proxy
jsonData:

View File

@ -181,7 +181,7 @@ It was released as a beta feature in Grafana 6.7. The feedback has been really p
## Stackdriver data source supports Service Monitoring
[Service monitoring](https://cloud.google.com/service-monitoring) in Google Cloud Platform (GCP) enables you to monitor based on Service Level Objectives (SLOs) for your GCP services. The new SLO query builder in the Stackdriver data source allows you to display SLO data in Grafana. Read more about it in the [Stackdriver data source documentation]({{< relref "../features/datasources/stackdriver/#slo-service-level-objective-queries" >}}).
[Service monitoring](https://cloud.google.com/service-monitoring) in Google Cloud Platform (GCP) enables you to monitor based on Service Level Objectives (SLOs) for your GCP services. The new SLO query builder in the Stackdriver data source allows you to display SLO data in Grafana. Read more about it in the [Stackdriver data source documentation]({{< relref "../features/datasources/cloudmonitoring/#slo-service-level-objective-queries" >}}).
## Time zone support

View File

@ -109,8 +109,8 @@
name: Azure Monitor
- link: /features/datasources/elasticsearch/
name: Elasticsearch
- link: /features/datasources/stackdriver/
name: Google Stackdriver
- link: /features/datasources/cloudmonitoring/
name: Google Cloud Monitoring
- link: /features/datasources/graphite/
name: Graphite
- link: /features/datasources/influxdb/

View File

@ -43,7 +43,7 @@ export interface QueryResultMeta {
* Legacy data source specific, should be moved to custom
* */
gmdMeta?: any[]; // used by cloudwatch
alignmentPeriod?: string; // used by stackdriver
alignmentPeriod?: string; // used by cloud monitoring
searchWords?: string[]; // used by log models and loki
limit?: number; // used by log models and loki
json?: boolean; // used to keep track of old json doc values

View File

@ -20,6 +20,7 @@ import (
_ "github.com/grafana/grafana/pkg/services/alerting/notifiers"
"github.com/grafana/grafana/pkg/setting"
_ "github.com/grafana/grafana/pkg/tsdb/azuremonitor"
_ "github.com/grafana/grafana/pkg/tsdb/cloudmonitoring"
_ "github.com/grafana/grafana/pkg/tsdb/cloudwatch"
_ "github.com/grafana/grafana/pkg/tsdb/elasticsearch"
_ "github.com/grafana/grafana/pkg/tsdb/graphite"
@ -28,7 +29,6 @@ import (
_ "github.com/grafana/grafana/pkg/tsdb/opentsdb"
_ "github.com/grafana/grafana/pkg/tsdb/postgres"
_ "github.com/grafana/grafana/pkg/tsdb/prometheus"
_ "github.com/grafana/grafana/pkg/tsdb/stackdriver"
_ "github.com/grafana/grafana/pkg/tsdb/testdatasource"
)

View File

@ -22,9 +22,11 @@ const (
DS_MSSQL = "mssql"
DS_ACCESS_DIRECT = "direct"
DS_ACCESS_PROXY = "proxy"
DS_STACKDRIVER = "stackdriver"
DS_AZURE_MONITOR = "grafana-azure-monitor-datasource"
DS_LOKI = "loki"
// Stackdriver was renamed Google Cloud monitoring 2020-05 but we keep
// "stackdriver" to avoid breaking changes in reporting.
DS_CLOUD_MONITORING = "stackdriver"
DS_AZURE_MONITOR = "grafana-azure-monitor-datasource"
DS_LOKI = "loki"
)
var (
@ -97,7 +99,7 @@ var knownDatasourcePlugins = map[string]bool{
DS_POSTGRES: true,
DS_MYSQL: true,
DS_MSSQL: true,
DS_STACKDRIVER: true,
DS_CLOUD_MONITORING: true,
DS_AZURE_MONITOR: true,
DS_LOKI: true,
"opennms": true,

View File

@ -60,8 +60,12 @@ func (fp *FrontendPluginBase) handleModuleDefaults() {
}
fp.IsCorePlugin = true
fp.Module = path.Join("app/plugins", fp.Type, fp.Id, "module")
fp.BaseUrl = path.Join("public/app/plugins", fp.Type, fp.Id)
// Previously there was an assumption that the plugin directory
// should be public/app/plugins/<plugin type>/<plugin id>
// However this can be an issue if the plugin directory should be renamed to something else
currentDir := path.Base(fp.PluginDir)
fp.Module = path.Join("app/plugins", fp.Type, currentDir, "module")
fp.BaseUrl = path.Join("public/app/plugins", fp.Type, currentDir)
}
func isExternalPlugin(pluginDir string) bool {

View File

@ -1,4 +1,4 @@
package stackdriver
package cloudmonitoring
import (
"context"
@ -9,7 +9,7 @@ import (
"github.com/grafana/grafana/pkg/tsdb"
)
func (e *StackdriverExecutor) executeAnnotationQuery(ctx context.Context, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
func (e *CloudMonitoringExecutor) executeAnnotationQuery(ctx context.Context, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
result := &tsdb.Response{
Results: make(map[string]*tsdb.QueryResult),
}
@ -34,7 +34,7 @@ func (e *StackdriverExecutor) executeAnnotationQuery(ctx context.Context, tsdbQu
return result, err
}
func (e *StackdriverExecutor) parseToAnnotations(queryRes *tsdb.QueryResult, data stackdriverResponse, query *stackdriverQuery, title string, text string, tags string) error {
func (e *CloudMonitoringExecutor) parseToAnnotations(queryRes *tsdb.QueryResult, data cloudMonitoringResponse, query *cloudMonitoringQuery, title string, text string, tags string) error {
annotations := make([]map[string]string, 0)
for _, series := range data.TimeSeries {

View File

@ -1,4 +1,4 @@
package stackdriver
package cloudmonitoring
import (
"testing"
@ -9,16 +9,16 @@ import (
. "github.com/smartystreets/goconvey/convey"
)
func TestStackdriverAnnotationQuery(t *testing.T) {
Convey("Stackdriver Annotation Query Executor", t, func() {
executor := &StackdriverExecutor{}
Convey("When parsing the stackdriver api response", func() {
func TestCloudMonitoringAnnotationQuery(t *testing.T) {
Convey("CloudMonitoring Annotation Query Executor", t, func() {
executor := &CloudMonitoringExecutor{}
Convey("When parsing the cloud monitoring api response", func() {
data, err := loadTestFile("./test-data/2-series-response-no-agg.json")
So(err, ShouldBeNil)
So(len(data.TimeSeries), ShouldEqual, 3)
res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "annotationQuery"}
query := &stackdriverQuery{}
query := &cloudMonitoringQuery{}
err = executor.parseToAnnotations(res, data, query, "atitle {{metric.label.instance_name}} {{metric.value}}", "atext {{resource.label.zone}}", "atag")
So(err, ShouldBeNil)

View File

@ -1,4 +1,4 @@
package stackdriver
package cloudmonitoring
import (
"context"
@ -48,34 +48,34 @@ const (
sloQueryType string = "slo"
)
// StackdriverExecutor executes queries for the Stackdriver datasource
type StackdriverExecutor struct {
// CloudMonitoringExecutor executes queries for the CloudMonitoring datasource
type CloudMonitoringExecutor struct {
httpClient *http.Client
dsInfo *models.DataSource
}
// NewStackdriverExecutor initializes a http client
func NewStackdriverExecutor(dsInfo *models.DataSource) (tsdb.TsdbQueryEndpoint, error) {
// NewCloudMonitoringExecutor initializes a http client
func NewCloudMonitoringExecutor(dsInfo *models.DataSource) (tsdb.TsdbQueryEndpoint, error) {
httpClient, err := dsInfo.GetHttpClient()
if err != nil {
return nil, err
}
return &StackdriverExecutor{
return &CloudMonitoringExecutor{
httpClient: httpClient,
dsInfo: dsInfo,
}, nil
}
func init() {
slog = log.New("tsdb.stackdriver")
tsdb.RegisterTsdbQueryEndpoint("stackdriver", NewStackdriverExecutor)
slog = log.New("tsdb.cloudMonitoring")
tsdb.RegisterTsdbQueryEndpoint("stackdriver", NewCloudMonitoringExecutor)
}
// Query takes in the frontend queries, parses them into the Stackdriver query format
// executes the queries against the Stackdriver API and parses the response into
// Query takes in the frontend queries, parses them into the CloudMonitoring query format
// executes the queries against the CloudMonitoring API and parses the response into
// the time series or table format
func (e *StackdriverExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
func (e *CloudMonitoringExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
var result *tsdb.Response
var err error
queryType := tsdbQuery.Queries[0].Model.Get("type").MustString("")
@ -94,7 +94,7 @@ func (e *StackdriverExecutor) Query(ctx context.Context, dsInfo *models.DataSour
return result, err
}
func (e *StackdriverExecutor) getGCEDefaultProject(ctx context.Context, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
func (e *CloudMonitoringExecutor) getGCEDefaultProject(ctx context.Context, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
result := &tsdb.Response{
Results: make(map[string]*tsdb.QueryResult),
}
@ -112,7 +112,7 @@ func (e *StackdriverExecutor) getGCEDefaultProject(ctx context.Context, tsdbQuer
return result, nil
}
func (e *StackdriverExecutor) executeTimeSeriesQuery(ctx context.Context, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
func (e *CloudMonitoringExecutor) executeTimeSeriesQuery(ctx context.Context, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
result := &tsdb.Response{
Results: make(map[string]*tsdb.QueryResult),
}
@ -137,8 +137,8 @@ func (e *StackdriverExecutor) executeTimeSeriesQuery(ctx context.Context, tsdbQu
return result, nil
}
func (e *StackdriverExecutor) buildQueries(tsdbQuery *tsdb.TsdbQuery) ([]*stackdriverQuery, error) {
stackdriverQueries := []*stackdriverQuery{}
func (e *CloudMonitoringExecutor) buildQueries(tsdbQuery *tsdb.TsdbQuery) ([]*cloudMonitoringQuery, error) {
cloudMonitoringQueries := []*cloudMonitoringQuery{}
startTime, err := tsdbQuery.TimeRange.ParseFrom()
if err != nil {
@ -157,14 +157,14 @@ func (e *StackdriverExecutor) buildQueries(tsdbQuery *tsdb.TsdbQuery) ([]*stackd
q := grafanaQuery{}
model, _ := query.Model.MarshalJSON()
if err := json.Unmarshal(model, &q); err != nil {
return nil, fmt.Errorf("could not unmarshal StackdriverQuery json: %w", err)
return nil, fmt.Errorf("could not unmarshal CloudMonitoringQuery json: %w", err)
}
var target string
params := url.Values{}
params.Add("interval.startTime", startTime.UTC().Format(time.RFC3339))
params.Add("interval.endTime", endTime.UTC().Format(time.RFC3339))
sq := &stackdriverQuery{
sq := &cloudMonitoringQuery{
RefID: query.RefId,
GroupBys: []string{},
}
@ -194,13 +194,13 @@ func (e *StackdriverExecutor) buildQueries(tsdbQuery *tsdb.TsdbQuery) ([]*stackd
sq.Params = params
if setting.Env == setting.DEV {
slog.Debug("Stackdriver request", "params", params)
slog.Debug("CloudMonitoring request", "params", params)
}
stackdriverQueries = append(stackdriverQueries, sq)
cloudMonitoringQueries = append(cloudMonitoringQueries, sq)
}
return stackdriverQueries, nil
return cloudMonitoringQueries, nil
}
func migrateLegacyQueryModel(query *tsdb.Query) {
@ -306,7 +306,7 @@ func calculateAlignmentPeriod(alignmentPeriod string, intervalMs int64, duration
alignmentPeriod = "+" + strconv.Itoa(alignmentPeriodValue) + "s"
}
if alignmentPeriod == "stackdriver-auto" {
if alignmentPeriod == "cloud-monitoring-auto" || alignmentPeriod == "stackdriver-auto" { // legacy
alignmentPeriodValue := int(math.Max(float64(durationSeconds), 60.0))
if alignmentPeriodValue < 60*60*23 {
alignmentPeriod = "+60s"
@ -320,23 +320,23 @@ func calculateAlignmentPeriod(alignmentPeriod string, intervalMs int64, duration
return alignmentPeriod
}
func (e *StackdriverExecutor) executeQuery(ctx context.Context, query *stackdriverQuery, tsdbQuery *tsdb.TsdbQuery) (*tsdb.QueryResult, stackdriverResponse, error) {
func (e *CloudMonitoringExecutor) executeQuery(ctx context.Context, query *cloudMonitoringQuery, tsdbQuery *tsdb.TsdbQuery) (*tsdb.QueryResult, cloudMonitoringResponse, error) {
queryResult := &tsdb.QueryResult{Meta: simplejson.New(), RefId: query.RefID}
projectName := query.ProjectName
if projectName == "" {
defaultProject, err := e.getDefaultProject(ctx)
if err != nil {
queryResult.Error = err
return queryResult, stackdriverResponse{}, nil
return queryResult, cloudMonitoringResponse{}, nil
}
projectName = defaultProject
slog.Info("No project name set on query, using project name from datasource", "projectName", projectName)
}
req, err := e.createRequest(ctx, e.dsInfo, query, fmt.Sprintf("stackdriver%s", "v3/projects/"+projectName+"/timeSeries"))
req, err := e.createRequest(ctx, e.dsInfo, query, fmt.Sprintf("cloudmonitoring%s", "v3/projects/"+projectName+"/timeSeries"))
if err != nil {
queryResult.Error = err
return queryResult, stackdriverResponse{}, nil
return queryResult, cloudMonitoringResponse{}, nil
}
req.URL.RawQuery = query.Params.Encode()
@ -350,7 +350,7 @@ func (e *StackdriverExecutor) executeQuery(ctx context.Context, query *stackdriv
}
}
span, ctx := opentracing.StartSpanFromContext(ctx, "stackdriver query")
span, ctx := opentracing.StartSpanFromContext(ctx, "cloudMonitoring query")
span.SetTag("target", query.Target)
span.SetTag("from", tsdbQuery.TimeRange.From)
span.SetTag("until", tsdbQuery.TimeRange.To)
@ -364,47 +364,47 @@ func (e *StackdriverExecutor) executeQuery(ctx context.Context, query *stackdriv
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header)); err != nil {
queryResult.Error = err
return queryResult, stackdriverResponse{}, nil
return queryResult, cloudMonitoringResponse{}, nil
}
res, err := ctxhttp.Do(ctx, e.httpClient, req)
if err != nil {
queryResult.Error = err
return queryResult, stackdriverResponse{}, nil
return queryResult, cloudMonitoringResponse{}, nil
}
data, err := e.unmarshalResponse(res)
if err != nil {
queryResult.Error = err
return queryResult, stackdriverResponse{}, nil
return queryResult, cloudMonitoringResponse{}, nil
}
return queryResult, data, nil
}
func (e *StackdriverExecutor) unmarshalResponse(res *http.Response) (stackdriverResponse, error) {
func (e *CloudMonitoringExecutor) unmarshalResponse(res *http.Response) (cloudMonitoringResponse, error) {
body, err := ioutil.ReadAll(res.Body)
defer res.Body.Close()
if err != nil {
return stackdriverResponse{}, err
return cloudMonitoringResponse{}, err
}
if res.StatusCode/100 != 2 {
slog.Error("Request failed", "status", res.Status, "body", string(body))
return stackdriverResponse{}, fmt.Errorf(string(body))
return cloudMonitoringResponse{}, fmt.Errorf(string(body))
}
var data stackdriverResponse
var data cloudMonitoringResponse
err = json.Unmarshal(body, &data)
if err != nil {
slog.Error("Failed to unmarshal Stackdriver response", "error", err, "status", res.Status, "body", string(body))
return stackdriverResponse{}, err
slog.Error("Failed to unmarshal CloudMonitoring response", "error", err, "status", res.Status, "body", string(body))
return cloudMonitoringResponse{}, err
}
return data, nil
}
func (e *StackdriverExecutor) parseResponse(queryRes *tsdb.QueryResult, data stackdriverResponse, query *stackdriverQuery) error {
func (e *CloudMonitoringExecutor) parseResponse(queryRes *tsdb.QueryResult, data cloudMonitoringResponse, query *cloudMonitoringQuery) error {
labels := make(map[string]map[string]bool)
for _, series := range data.TimeSeries {
@ -570,7 +570,7 @@ func containsLabel(labels []string, newLabel string) bool {
return false
}
func formatLegendKeys(metricType string, defaultMetricName string, labels map[string]string, additionalLabels map[string]string, query *stackdriverQuery) string {
func formatLegendKeys(metricType string, defaultMetricName string, labels map[string]string, additionalLabels map[string]string, query *cloudMonitoringQuery) string {
if query.AliasBy == "" {
return defaultMetricName
}
@ -639,7 +639,7 @@ func replaceWithMetricPart(metaPartName string, metricType string) []byte {
return nil
}
func calcBucketBound(bucketOptions stackdriverBucketOptions, n int) string {
func calcBucketBound(bucketOptions cloudMonitoringBucketOptions, n int) string {
bucketBound := "0"
if n == 0 {
return bucketBound
@ -655,7 +655,7 @@ func calcBucketBound(bucketOptions stackdriverBucketOptions, n int) string {
return bucketBound
}
func (e *StackdriverExecutor) createRequest(ctx context.Context, dsInfo *models.DataSource, query *stackdriverQuery, proxyPass string) (*http.Request, error) {
func (e *CloudMonitoringExecutor) createRequest(ctx context.Context, dsInfo *models.DataSource, query *cloudMonitoringQuery, proxyPass string) (*http.Request, error) {
u, err := url.Parse(dsInfo.Url)
if err != nil {
return nil, err
@ -674,23 +674,23 @@ func (e *StackdriverExecutor) createRequest(ctx context.Context, dsInfo *models.
// find plugin
plugin, ok := plugins.DataSources[dsInfo.Type]
if !ok {
return nil, errors.New("Unable to find datasource plugin Stackdriver")
return nil, errors.New("Unable to find datasource plugin CloudMonitoring")
}
var stackdriverRoute *plugins.AppPluginRoute
var cloudMonitoringRoute *plugins.AppPluginRoute
for _, route := range plugin.Routes {
if route.Path == "stackdriver" {
stackdriverRoute = route
if route.Path == "cloudmonitoring" {
cloudMonitoringRoute = route
break
}
}
pluginproxy.ApplyRoute(ctx, req, proxyPass, stackdriverRoute, dsInfo)
pluginproxy.ApplyRoute(ctx, req, proxyPass, cloudMonitoringRoute, dsInfo)
return req, nil
}
func (e *StackdriverExecutor) getDefaultProject(ctx context.Context) (string, error) {
func (e *CloudMonitoringExecutor) getDefaultProject(ctx context.Context) (string, error) {
authenticationType := e.dsInfo.JsonData.Get("authenticationType").MustString(jwtAuthentication)
if authenticationType == gceAuthentication {
defaultCredentials, err := google.FindDefaultCredentials(ctx, "https://www.googleapis.com/auth/monitoring.read")

View File

@ -1,4 +1,4 @@
package stackdriver
package cloudmonitoring
import (
"encoding/json"
@ -15,11 +15,11 @@ import (
. "github.com/smartystreets/goconvey/convey"
)
func TestStackdriver(t *testing.T) {
Convey("Stackdriver", t, func() {
executor := &StackdriverExecutor{}
func TestCloudMonitoring(t *testing.T) {
Convey("Google Cloud Monitoring", t, func() {
executor := &CloudMonitoringExecutor{}
Convey("Parse migrated queries from frontend and build Stackdriver API queries", func() {
Convey("Parse migrated queries from frontend and build Google Cloud Monitoring API queries", func() {
fromStart := time.Date(2018, 3, 15, 13, 0, 0, 0, time.UTC).In(time.Local)
tsdbQuery := &tsdb.TsdbQuery{
TimeRange: &tsdb.TimeRange{
@ -92,7 +92,61 @@ func TestStackdriver(t *testing.T) {
})
})
Convey("and alignmentPeriod is set to stackdriver-auto", func() {
Convey("and alignmentPeriod is set to cloud-monitoring-auto", func() { // legacy
Convey("and range is two hours", func() {
tsdbQuery.TimeRange.From = "1538033322461"
tsdbQuery.TimeRange.To = "1538040522461"
tsdbQuery.Queries[0].Model = simplejson.NewFromAny(map[string]interface{}{
"target": "target",
"alignmentPeriod": "cloud-monitoring-auto",
})
queries, err := executor.buildQueries(tsdbQuery)
So(err, ShouldBeNil)
So(queries[0].Params["aggregation.alignmentPeriod"][0], ShouldEqual, `+60s`)
})
Convey("and range is 22 hours", func() {
tsdbQuery.TimeRange.From = "1538034524922"
tsdbQuery.TimeRange.To = "1538113724922"
tsdbQuery.Queries[0].Model = simplejson.NewFromAny(map[string]interface{}{
"target": "target",
"alignmentPeriod": "cloud-monitoring-auto",
})
queries, err := executor.buildQueries(tsdbQuery)
So(err, ShouldBeNil)
So(queries[0].Params["aggregation.alignmentPeriod"][0], ShouldEqual, `+60s`)
})
Convey("and range is 23 hours", func() {
tsdbQuery.TimeRange.From = "1538034567985"
tsdbQuery.TimeRange.To = "1538117367985"
tsdbQuery.Queries[0].Model = simplejson.NewFromAny(map[string]interface{}{
"target": "target",
"alignmentPeriod": "cloud-monitoring-auto",
})
queries, err := executor.buildQueries(tsdbQuery)
So(err, ShouldBeNil)
So(queries[0].Params["aggregation.alignmentPeriod"][0], ShouldEqual, `+300s`)
})
Convey("and range is 7 days", func() {
tsdbQuery.TimeRange.From = "1538036324073"
tsdbQuery.TimeRange.To = "1538641124073"
tsdbQuery.Queries[0].Model = simplejson.NewFromAny(map[string]interface{}{
"target": "target",
"alignmentPeriod": "cloud-monitoring-auto",
})
queries, err := executor.buildQueries(tsdbQuery)
So(err, ShouldBeNil)
So(queries[0].Params["aggregation.alignmentPeriod"][0], ShouldEqual, `+3600s`)
})
})
Convey("and alignmentPeriod is set to stackdriver-auto", func() { // legacy
Convey("and range is two hours", func() {
tsdbQuery.TimeRange.From = "1538033322461"
tsdbQuery.TimeRange.To = "1538040522461"
@ -208,7 +262,7 @@ func TestStackdriver(t *testing.T) {
})
Convey("Parse queries from frontend and build Stackdriver API queries", func() {
Convey("Parse queries from frontend and build Google Cloud Monitoring API queries", func() {
fromStart := time.Date(2018, 3, 15, 13, 0, 0, 0, time.UTC).In(time.Local)
tsdbQuery := &tsdb.TsdbQuery{
TimeRange: &tsdb.TimeRange{
@ -301,14 +355,14 @@ func TestStackdriver(t *testing.T) {
})
})
Convey("Parse stackdriver response in the time series format", func() {
Convey("Parse cloud monitoring response in the time series format", func() {
Convey("when data from query aggregated to one time series", func() {
data, err := loadTestFile("./test-data/1-series-response-agg-one-metric.json")
So(err, ShouldBeNil)
So(len(data.TimeSeries), ShouldEqual, 1)
res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "A"}
query := &stackdriverQuery{}
query := &cloudMonitoringQuery{}
err = executor.parseResponse(res, data, query)
So(err, ShouldBeNil)
@ -334,7 +388,7 @@ func TestStackdriver(t *testing.T) {
So(len(data.TimeSeries), ShouldEqual, 3)
res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "A"}
query := &stackdriverQuery{}
query := &cloudMonitoringQuery{}
err = executor.parseResponse(res, data, query)
So(err, ShouldBeNil)
@ -376,7 +430,7 @@ func TestStackdriver(t *testing.T) {
So(len(data.TimeSeries), ShouldEqual, 3)
res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "A"}
query := &stackdriverQuery{GroupBys: []string{"metric.label.instance_name", "resource.label.zone"}}
query := &cloudMonitoringQuery{GroupBys: []string{"metric.label.instance_name", "resource.label.zone"}}
err = executor.parseResponse(res, data, query)
So(err, ShouldBeNil)
@ -397,7 +451,7 @@ func TestStackdriver(t *testing.T) {
Convey("and the alias pattern is for metric type, a metric label and a resource label", func() {
query := &stackdriverQuery{AliasBy: "{{metric.type}} - {{metric.label.instance_name}} - {{resource.label.zone}}", GroupBys: []string{"metric.label.instance_name", "resource.label.zone"}}
query := &cloudMonitoringQuery{AliasBy: "{{metric.type}} - {{metric.label.instance_name}} - {{resource.label.zone}}", GroupBys: []string{"metric.label.instance_name", "resource.label.zone"}}
err = executor.parseResponse(res, data, query)
So(err, ShouldBeNil)
@ -411,7 +465,7 @@ func TestStackdriver(t *testing.T) {
Convey("and the alias pattern is for metric name", func() {
query := &stackdriverQuery{AliasBy: "metric {{metric.name}} service {{metric.service}}", GroupBys: []string{"metric.label.instance_name", "resource.label.zone"}}
query := &cloudMonitoringQuery{AliasBy: "metric {{metric.name}} service {{metric.service}}", GroupBys: []string{"metric.label.instance_name", "resource.label.zone"}}
err = executor.parseResponse(res, data, query)
So(err, ShouldBeNil)
@ -430,7 +484,7 @@ func TestStackdriver(t *testing.T) {
So(len(data.TimeSeries), ShouldEqual, 1)
res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "A"}
query := &stackdriverQuery{AliasBy: "{{bucket}}"}
query := &cloudMonitoringQuery{AliasBy: "{{bucket}}"}
err = executor.parseResponse(res, data, query)
So(err, ShouldBeNil)
@ -477,7 +531,7 @@ func TestStackdriver(t *testing.T) {
So(len(data.TimeSeries), ShouldEqual, 1)
res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "A"}
query := &stackdriverQuery{AliasBy: "{{bucket}}"}
query := &cloudMonitoringQuery{AliasBy: "{{bucket}}"}
err = executor.parseResponse(res, data, query)
So(err, ShouldBeNil)
@ -517,7 +571,7 @@ func TestStackdriver(t *testing.T) {
So(len(data.TimeSeries), ShouldEqual, 3)
res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "A"}
query := &stackdriverQuery{AliasBy: "{{bucket}}"}
query := &cloudMonitoringQuery{AliasBy: "{{bucket}}"}
err = executor.parseResponse(res, data, query)
labels := res.Meta.Get("labels").Interface().(map[string][]string)
So(err, ShouldBeNil)
@ -556,7 +610,7 @@ func TestStackdriver(t *testing.T) {
Convey("and systemlabel contains key with array of string", func() {
res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "A"}
query := &stackdriverQuery{AliasBy: "{{metadata.system_labels.test}}"}
query := &cloudMonitoringQuery{AliasBy: "{{metadata.system_labels.test}}"}
err = executor.parseResponse(res, data, query)
So(err, ShouldBeNil)
So(len(res.Series), ShouldEqual, 3)
@ -568,7 +622,7 @@ func TestStackdriver(t *testing.T) {
Convey("and systemlabel contains key with array of string2", func() {
res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "A"}
query := &stackdriverQuery{AliasBy: "{{metadata.system_labels.test2}}"}
query := &cloudMonitoringQuery{AliasBy: "{{metadata.system_labels.test2}}"}
err = executor.parseResponse(res, data, query)
So(err, ShouldBeNil)
So(len(res.Series), ShouldEqual, 3)
@ -584,7 +638,7 @@ func TestStackdriver(t *testing.T) {
Convey("and alias by is expanded", func() {
res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "A"}
query := &stackdriverQuery{
query := &cloudMonitoringQuery{
ProjectName: "test-proj",
Selector: "select_slo_compliance",
Service: "test-service",
@ -604,7 +658,7 @@ func TestStackdriver(t *testing.T) {
Convey("and alias by is expanded", func() {
res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "A"}
query := &stackdriverQuery{
query := &cloudMonitoringQuery{
ProjectName: "test-proj",
Selector: "select_slo_compliance",
Service: "test-service",
@ -710,8 +764,8 @@ func TestStackdriver(t *testing.T) {
})
}
func loadTestFile(path string) (stackdriverResponse, error) {
var data stackdriverResponse
func loadTestFile(path string) (cloudMonitoringResponse, error) {
var data cloudMonitoringResponse
jsonBody, err := ioutil.ReadFile(path)
if err != nil {

View File

@ -1,4 +1,4 @@
package stackdriver
package cloudmonitoring
import (
"net/url"
@ -6,7 +6,7 @@ import (
)
type (
stackdriverQuery struct {
cloudMonitoringQuery struct {
Target string
Params url.Values
RefID string
@ -48,7 +48,7 @@ type (
SloQuery sloQuery
}
stackdriverBucketOptions struct {
cloudMonitoringBucketOptions struct {
LinearBuckets *struct {
NumFiniteBuckets int64 `json:"numFiniteBuckets"`
Width int64 `json:"width"`
@ -64,7 +64,7 @@ type (
} `json:"explicitBuckets"`
}
stackdriverResponse struct {
cloudMonitoringResponse struct {
TimeSeries []struct {
Metric struct {
Labels map[string]string `json:"labels"`
@ -95,8 +95,8 @@ type (
Min int `json:"min"`
Max int `json:"max"`
} `json:"range"`
BucketOptions stackdriverBucketOptions `json:"bucketOptions"`
BucketCounts []string `json:"bucketCounts"`
BucketOptions cloudMonitoringBucketOptions `json:"bucketOptions"`
BucketCounts []string `json:"bucketCounts"`
Examplars []struct {
Value float64 `json:"value"`
Timestamp string `json:"timestamp"`

View File

@ -1,6 +1,6 @@
import { react2AngularDirective } from 'app/core/utils/react2angular';
import { QueryEditor as StackdriverQueryEditor } from 'app/plugins/datasource/stackdriver/components/QueryEditor';
import { AnnotationQueryEditor as StackdriverAnnotationQueryEditor } from 'app/plugins/datasource/stackdriver/components/AnnotationQueryEditor';
import { QueryEditor as CloudMonitoringQueryEditor } from 'app/plugins/datasource/cloud-monitoring/components/QueryEditor';
import { AnnotationQueryEditor as CloudMonitoringAnnotationQueryEditor } from 'app/plugins/datasource/cloud-monitoring/components/AnnotationQueryEditor';
import { AnnotationQueryEditor as CloudWatchAnnotationQueryEditor } from 'app/plugins/datasource/cloudwatch/components/AnnotationQueryEditor';
import PageHeader from './components/PageHeader/PageHeader';
import EmptyListCTA from './components/EmptyListCTA/EmptyListCTA';
@ -118,7 +118,7 @@ export function registerAngularDirectives() {
'placeholder',
['variables', { watchDepth: 'reference' }],
]);
react2AngularDirective('stackdriverQueryEditor', StackdriverQueryEditor, [
react2AngularDirective('cloudMonitoringQueryEditor', CloudMonitoringQueryEditor, [
'target',
'onQueryChange',
'onExecuteQuery',
@ -126,7 +126,7 @@ export function registerAngularDirectives() {
['datasource', { watchDepth: 'reference' }],
['templateSrv', { watchDepth: 'reference' }],
]);
react2AngularDirective('stackdriverAnnotationQueryEditor', StackdriverAnnotationQueryEditor, [
react2AngularDirective('cloudMonitoringAnnotationQueryEditor', CloudMonitoringAnnotationQueryEditor, [
'target',
'onQueryChange',
['datasource', { watchDepth: 'reference' }],

View File

@ -29,8 +29,8 @@ const mssqlPlugin = async () =>
await import(/* webpackChunkName: "mssqlPlugin" */ 'app/plugins/datasource/mssql/module');
const testDataDSPlugin = async () =>
await import(/* webpackChunkName: "testDataDSPlugin" */ 'app/plugins/datasource/testdata/module');
const stackdriverPlugin = async () =>
await import(/* webpackChunkName: "stackdriverPlugin" */ 'app/plugins/datasource/stackdriver/module');
const cloudMonitoringPlugin = async () =>
await import(/* webpackChunkName: "cloudMonitoringPlugin" */ 'app/plugins/datasource/cloud-monitoring/module');
const azureMonitorPlugin = async () =>
await import(
/* webpackChunkName: "azureMonitorPlugin" */ 'app/plugins/datasource/grafana-azure-monitor-datasource/module'
@ -74,7 +74,7 @@ const builtInPlugins: any = {
'app/plugins/datasource/mssql/module': mssqlPlugin,
'app/plugins/datasource/prometheus/module': prometheusPlugin,
'app/plugins/datasource/testdata/module': testDataDSPlugin,
'app/plugins/datasource/stackdriver/module': stackdriverPlugin,
'app/plugins/datasource/cloud-monitoring/module': cloudMonitoringPlugin,
'app/plugins/datasource/grafana-azure-monitor-datasource/module': azureMonitorPlugin,
'app/plugins/panel/text/module': textPanel,

View File

@ -1,6 +1,6 @@
import isString from 'lodash/isString';
import { alignmentPeriods, ValueTypes, MetricKind, selectors } from './constants';
import StackdriverDatasource from './datasource';
import CloudMonitoringDatasource from './datasource';
import { MetricFindQueryTypes, VariableQueryData } from './types';
import { SelectableValue } from '@grafana/data';
import {
@ -11,8 +11,8 @@ import {
getLabelKeys,
} from './functions';
export default class StackdriverMetricFindQuery {
constructor(private datasource: StackdriverDatasource) {}
export default class CloudMonitoringMetricFindQuery {
constructor(private datasource: CloudMonitoringDatasource) {}
async execute(query: VariableQueryData) {
try {
@ -49,7 +49,7 @@ export default class StackdriverMetricFindQuery {
return [];
}
} catch (error) {
console.error(`Could not run StackdriverMetricFindQuery ${query}`, error);
console.error(`Could not run CloudMonitoringMetricFindQuery ${query}`, error);
return [];
}
}

View File

@ -0,0 +1,7 @@
# Google Cloud Monitoring Data Source - Native Plugin (formerly named Stackdriver)
Grafana ships with built-in support for Google Cloud Monitoring. You just have to add it as a data source and you will be ready to build dashboards for your Cloud Monitoring metrics.
Read more about it here:
[https://grafana.com/docs/grafana/latest/features/datasources/cloudmonitoring/](https://grafana.com/docs/grafana/latest/features/datasources/cloudmonitoring/)

View File

@ -1,7 +1,7 @@
import { TemplateSrv } from 'app/features/templating/template_srv';
import { AnnotationTarget } from './types';
export class StackdriverAnnotationsQueryCtrl {
export class CloudMonitoringAnnotationsQueryCtrl {
static templateUrl = 'partials/annotations.editor.html';
annotation: any;
templateSrv: TemplateSrv;

View File

@ -25,7 +25,7 @@ describe('api', () => {
let api: Api;
let res: Array<SelectableValue<string>>;
beforeEach(async () => {
api = new Api('/stackdriver/');
api = new Api('/cloudmonitoring/');
api.cache['some-resource'] = response;
res = await api.get('some-resource');
});
@ -41,7 +41,7 @@ describe('api', () => {
let api: Api;
let res: Array<SelectableValue<string>>;
beforeEach(async () => {
api = new Api('/stackdriver/');
api = new Api('/cloudmonitoring/');
res = await api.get('some-resource');
});
@ -56,7 +56,7 @@ describe('api', () => {
let api: Api;
let res: Array<SelectableValue<string>>;
beforeEach(async () => {
api = new Api('/stackdriver/');
api = new Api('/cloudmonitoring/');
api.cache['some-resource'] = response;
res = await api.get('some-resource', { useCache: false });
});

View File

@ -3,7 +3,7 @@ import { CoreEvents } from 'app/types';
import { SelectableValue } from '@grafana/data';
import { getBackendSrv } from '@grafana/runtime';
import { formatStackdriverError } from './functions';
import { formatCloudMonitoringError } from './functions';
import { MetricDescriptor } from './types';
interface Options {
@ -50,7 +50,7 @@ export default class Api {
return res;
} catch (error) {
appEvents.emit(CoreEvents.dsRequestError, { error: { data: { error: formatStackdriverError(error) } } });
appEvents.emit(CoreEvents.dsRequestError, { error: { data: { error: formatCloudMonitoringError(error) } } });
return [];
}
}

View File

@ -30,6 +30,7 @@ export const AlignmentPeriods: FC<Props> = ({
...ap,
label: ap.text,
}));
const visibleOptions = options.filter(ap => !ap.hidden);
return (
<>
@ -46,7 +47,7 @@ export const AlignmentPeriods: FC<Props> = ({
{
label: 'Aggregations',
expanded: true,
options: options,
options: visibleOptions,
},
]}
placeholder="Select Alignment"

View File

@ -5,7 +5,7 @@ const { Input } = LegacyForms;
import { TemplateSrv } from 'app/features/templating/template_srv';
import { SelectableValue } from '@grafana/data';
import StackdriverDatasource from '../datasource';
import CloudMonitoringDatasource from '../datasource';
import { Metrics, LabelFilter, AnnotationsHelp, Project } from './';
import { toOption } from '../functions';
import { AnnotationTarget, MetricDescriptor } from '../types';
@ -13,7 +13,7 @@ import { AnnotationTarget, MetricDescriptor } from '../types';
export interface Props {
onQueryChange: (target: AnnotationTarget) => void;
target: AnnotationTarget;
datasource: StackdriverDatasource;
datasource: CloudMonitoringDatasource;
templateSrv: TemplateSrv;
}

View File

@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import { Project, Aggregations, Metrics, LabelFilter, GroupBys, Alignments, AlignmentPeriods, AliasBy } from '.';
import { MetricQuery, MetricDescriptor } from '../types';
import { getAlignmentPickerData } from '../functions';
import StackdriverDatasource from '../datasource';
import CloudMonitoringDatasource from '../datasource';
import { SelectableValue } from '@grafana/data';
export interface Props {
@ -12,7 +12,7 @@ export interface Props {
onChange: (query: MetricQuery) => void;
onRunQuery: () => void;
query: MetricQuery;
datasource: StackdriverDatasource;
datasource: CloudMonitoringDatasource;
}
interface State {
@ -31,7 +31,7 @@ export const defaultQuery: MetricQuery = {
valueType: '',
unit: '',
crossSeriesReducer: 'REDUCE_MEAN',
alignmentPeriod: 'stackdriver-auto',
alignmentPeriod: 'cloud-monitoring-auto',
perSeriesAligner: 'ALIGN_MEAN',
groupBys: [],
filters: [],

View File

@ -3,7 +3,7 @@ import _ from 'lodash';
import { TemplateSrv } from 'app/features/templating/template_srv';
import { SelectableValue } from '@grafana/data';
import StackdriverDatasource from '../datasource';
import CloudMonitoringDatasource from '../datasource';
import { Segment } from '@grafana/ui';
import { MetricDescriptor } from '../types';
@ -11,7 +11,7 @@ export interface Props {
onChange: (metricDescriptor: MetricDescriptor) => void;
templateSrv: TemplateSrv;
templateVariableOptions: Array<SelectableValue<string>>;
datasource: StackdriverDatasource;
datasource: CloudMonitoringDatasource;
projectName: string;
metricType: string;
children?: (renderProps: any) => JSX.Element;

View File

@ -1,10 +1,10 @@
import React from 'react';
import { SelectableValue } from '@grafana/data';
import { SegmentAsync } from '@grafana/ui';
import StackdriverDatasource from '../datasource';
import CloudMonitoringDatasource from '../datasource';
export interface Props {
datasource: StackdriverDatasource;
datasource: CloudMonitoringDatasource;
onChange: (projectName: string) => void;
templateVariableOptions: Array<SelectableValue<string>>;
projectName: string;

View File

@ -2,14 +2,14 @@ import React, { PureComponent } from 'react';
import appEvents from 'app/core/app_events';
import { CoreEvents } from 'app/types';
import { MetricQueryEditor, QueryTypeSelector, SLOQueryEditor, Help } from './';
import { StackdriverQuery, MetricQuery, QueryType, SLOQuery } from '../types';
import { CloudMonitoringQuery, MetricQuery, QueryType, SLOQuery } from '../types';
import { defaultQuery } from './MetricQueryEditor';
import { defaultQuery as defaultSLOQuery } from './SLOQueryEditor';
import { toOption, formatStackdriverError } from '../functions';
import StackdriverDatasource from '../datasource';
import { toOption, formatCloudMonitoringError } from '../functions';
import CloudMonitoringDatasource from '../datasource';
import { ExploreQueryFieldProps } from '@grafana/data';
export type Props = ExploreQueryFieldProps<StackdriverDatasource, StackdriverQuery>;
export type Props = ExploreQueryFieldProps<CloudMonitoringDatasource, CloudMonitoringQuery>;
interface State {
lastQueryError: string;
@ -53,7 +53,7 @@ export class QueryEditor extends PureComponent<Props, State> {
}
onDataError(error: any) {
this.setState({ lastQueryError: formatStackdriverError(error) });
this.setState({ lastQueryError: formatCloudMonitoringError(error) });
}
onQueryChange(prop: string, value: any) {

View File

@ -4,7 +4,7 @@ import { SelectableValue } from '@grafana/data';
import { selectors } from '../constants';
import { Project, AlignmentPeriods, AliasBy, QueryInlineField } from '.';
import { SLOQuery } from '../types';
import StackdriverDatasource from '../datasource';
import CloudMonitoringDatasource from '../datasource';
export interface Props {
usedAlignmentPeriod: string;
@ -12,12 +12,12 @@ export interface Props {
onChange: (query: SLOQuery) => void;
onRunQuery: () => void;
query: SLOQuery;
datasource: StackdriverDatasource;
datasource: CloudMonitoringDatasource;
}
export const defaultQuery: SLOQuery = {
projectName: '',
alignmentPeriod: 'stackdriver-auto',
alignmentPeriod: 'cloud-monitoring-auto',
aliasBy: '',
selectorName: 'select_slo_health',
serviceId: '',

View File

@ -1,7 +1,7 @@
import React from 'react';
// @ts-ignore
import renderer from 'react-test-renderer';
import { StackdriverVariableQueryEditor } from './VariableQueryEditor';
import { CloudMonitoringVariableQueryEditor } from './VariableQueryEditor';
import { VariableQueryProps } from 'app/types/plugins';
import { MetricFindQueryTypes } from '../types';
import { VariableModel } from 'app/features/variables/types';
@ -41,7 +41,7 @@ const props: VariableQueryProps = {
describe('VariableQueryEditor', () => {
it('renders correctly', () => {
const tree = renderer.create(<StackdriverVariableQueryEditor {...props} />).toJSON();
const tree = renderer.create(<CloudMonitoringVariableQueryEditor {...props} />).toJSON();
expect(tree).toMatchSnapshot();
});
@ -49,10 +49,10 @@ describe('VariableQueryEditor', () => {
// these test need to be updated to reflect the changes from old variables system to new
it('should trigger a query using the first query type in the array', done => {
props.onChange = (query, definition) => {
expect(definition).toBe('Stackdriver - Projects');
expect(definition).toBe('Google Cloud Monitoring - Projects');
done();
};
renderer.create(<StackdriverVariableQueryEditor {...props} />).toJSON();
renderer.create(<CloudMonitoringVariableQueryEditor {...props} />).toJSON();
});
});
@ -61,10 +61,10 @@ describe('VariableQueryEditor', () => {
it('should trigger new query using the saved query type', done => {
props.query = { selectedQueryType: MetricFindQueryTypes.LabelKeys };
props.onChange = (query, definition) => {
expect(definition).toBe('Stackdriver - Label Keys');
expect(definition).toBe('Google Cloud Monitoring - Label Keys');
done();
};
renderer.create(<StackdriverVariableQueryEditor {...props} />).toJSON();
renderer.create(<CloudMonitoringVariableQueryEditor {...props} />).toJSON();
});
});
});

View File

@ -4,7 +4,7 @@ import { SimpleSelect } from './';
import { extractServicesFromMetricDescriptors, getLabelKeys, getMetricTypes } from '../functions';
import { MetricFindQueryTypes, VariableQueryData } from '../types';
export class StackdriverVariableQueryEditor extends PureComponent<VariableQueryProps, VariableQueryData> {
export class CloudMonitoringVariableQueryEditor extends PureComponent<VariableQueryProps, VariableQueryData> {
queryTypes: Array<{ value: string; name: string }> = [
{ value: MetricFindQueryTypes.Projects, name: 'Projects' },
{ value: MetricFindQueryTypes.Services, name: 'Services' },
@ -88,7 +88,7 @@ export class StackdriverVariableQueryEditor extends PureComponent<VariableQueryP
onPropsChange = () => {
const { metricDescriptors, labels, metricTypes, services, ...queryModel } = this.state;
const query = this.queryTypes.find(q => q.value === this.state.selectedQueryType);
this.props.onChange(queryModel, `Stackdriver - ${query.name}`);
this.props.onChange(queryModel, `Google Cloud Monitoring - ${query.name}`);
};
async onQueryTypeChange(queryType: string) {

View File

@ -8,8 +8,8 @@ export interface JWT {
project_id: string;
}
export class StackdriverConfigCtrl {
static templateUrl = 'public/app/plugins/datasource/stackdriver/partials/config.html';
export class CloudMonitoringConfigCtrl {
static templateUrl = 'public/app/plugins/datasource/cloud-monitoring/partials/config.html';
datasourceSrv: DatasourceSrv;
current: any;
meta: any;

View File

@ -231,7 +231,8 @@ export const aggOptions = [
export const alignmentPeriods = [
{ text: 'grafana auto', value: 'grafana-auto' },
{ text: 'stackdriver auto', value: 'stackdriver-auto' },
{ text: 'stackdriver auto', value: 'stackdriver-auto', hidden: true },
{ text: 'cloud monitoring auto', value: 'cloud-monitoring-auto' },
{ text: '1m', value: '+60s' },
{ text: '2m', value: '+120s' },
{ text: '5m', value: '+300s' },
@ -245,7 +246,7 @@ export const alignmentPeriods = [
{ text: '1w', value: '+604800s' },
];
export const stackdriverUnitMappings = {
export const cloudMonitoringUnitMappings = {
bit: 'bits',
By: 'bytes',
s: 's',

View File

@ -11,31 +11,38 @@ import {
import { TemplateSrv } from 'app/features/templating/template_srv';
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { StackdriverQuery, MetricDescriptor, StackdriverOptions, Filter, VariableQueryData, QueryType } from './types';
import { stackdriverUnitMappings } from './constants';
import {
CloudMonitoringQuery,
MetricDescriptor,
CloudMonitoringOptions,
Filter,
VariableQueryData,
QueryType,
} from './types';
import { cloudMonitoringUnitMappings } from './constants';
import API from './api';
import StackdriverMetricFindQuery from './StackdriverMetricFindQuery';
import CloudMonitoringMetricFindQuery from './CloudMonitoringMetricFindQuery';
export default class StackdriverDatasource extends DataSourceApi<StackdriverQuery, StackdriverOptions> {
export default class CloudMonitoringDatasource extends DataSourceApi<CloudMonitoringQuery, CloudMonitoringOptions> {
api: API;
authenticationType: string;
/** @ngInject */
constructor(
private instanceSettings: DataSourceInstanceSettings<StackdriverOptions>,
private instanceSettings: DataSourceInstanceSettings<CloudMonitoringOptions>,
public templateSrv: TemplateSrv,
private timeSrv: TimeSrv
) {
super(instanceSettings);
this.authenticationType = instanceSettings.jsonData.authenticationType || 'jwt';
this.api = new API(`${instanceSettings.url!}/stackdriver/v3/projects/`);
this.api = new API(`${instanceSettings.url!}/cloudmonitoring/v3/projects/`);
}
get variables() {
return this.templateSrv.getVariables().map(v => `$${v.name}`);
}
async query(options: DataQueryRequest<StackdriverQuery>): Promise<DataQueryResponse> {
async query(options: DataQueryRequest<CloudMonitoringQuery>): Promise<DataQueryResponse> {
const result: DataQueryResponse[] = [];
const data = await this.getTimeSeries(options);
if (data.results) {
@ -107,11 +114,11 @@ export default class StackdriverDatasource extends DataSourceApi<StackdriverQuer
async metricFindQuery(query: VariableQueryData) {
await this.ensureGCEDefaultProject();
const stackdriverMetricFindQuery = new StackdriverMetricFindQuery(this);
return stackdriverMetricFindQuery.execute(query);
const cloudMonitoringMetricFindQuery = new CloudMonitoringMetricFindQuery(this);
return cloudMonitoringMetricFindQuery.execute(query);
}
async getTimeSeries(options: DataQueryRequest<StackdriverQuery>) {
async getTimeSeries(options: DataQueryRequest<CloudMonitoringQuery>) {
await this.ensureGCEDefaultProject();
const queries = options.targets
.map(this.migrateQuery)
@ -147,20 +154,20 @@ export default class StackdriverDatasource extends DataSourceApi<StackdriverQuer
},
],
range: this.timeSrv.timeRange(),
} as DataQueryRequest<StackdriverQuery>);
} as DataQueryRequest<CloudMonitoringQuery>);
const result = response.results[refId];
return result && result.meta ? result.meta.labels : {};
}
async testDatasource() {
let status, message;
const defaultErrorMessage = 'Cannot connect to Stackdriver API';
const defaultErrorMessage = 'Cannot connect to Google Cloud Monitoring API';
try {
await this.ensureGCEDefaultProject();
const response = await this.api.test(this.getDefaultProject());
if (response.status === 200) {
status = 'success';
message = 'Successfully queried the Stackdriver API.';
message = 'Successfully queried the Google Cloud Monitoring API.';
} else {
status = 'error';
message = response.statusText ? response.statusText : defaultErrorMessage;
@ -170,7 +177,7 @@ export default class StackdriverDatasource extends DataSourceApi<StackdriverQuer
if (_.isString(error)) {
message = error;
} else {
message = 'Stackdriver: ';
message = 'Google Cloud Monitoring: ';
message += error.statusText ? error.statusText : defaultErrorMessage;
if (error.data && error.data.error && error.data.error.code) {
message += ': ' + error.data.error.code + '. ' + error.data.error.message;
@ -272,7 +279,7 @@ export default class StackdriverDatasource extends DataSourceApi<StackdriverQuer
});
}
migrateQuery(query: StackdriverQuery): StackdriverQuery {
migrateQuery(query: CloudMonitoringQuery): CloudMonitoringQuery {
if (!query.hasOwnProperty('metricQuery')) {
const { hide, refId, datasource, key, queryType, maxLines, metric, ...rest } = query as any;
return {
@ -297,7 +304,7 @@ export default class StackdriverDatasource extends DataSourceApi<StackdriverQuer
}, {});
}
shouldRunQuery(query: StackdriverQuery): boolean {
shouldRunQuery(query: CloudMonitoringQuery): boolean {
if (query.hide) {
return false;
}
@ -313,8 +320,8 @@ export default class StackdriverDatasource extends DataSourceApi<StackdriverQuer
}
prepareTimeSeriesQuery(
{ metricQuery, refId, queryType, sloQuery }: StackdriverQuery,
{ scopedVars, intervalMs }: DataQueryRequest<StackdriverQuery>
{ metricQuery, refId, queryType, sloQuery }: CloudMonitoringQuery,
{ scopedVars, intervalMs }: DataQueryRequest<CloudMonitoringQuery>
) {
return {
datasourceId: this.id,
@ -373,9 +380,9 @@ export default class StackdriverDatasource extends DataSourceApi<StackdriverQuer
resolvePanelUnitFromTargets(targets: any) {
let unit;
if (targets.length > 0 && targets.every((t: any) => t.unit === targets[0].unit)) {
if (stackdriverUnitMappings.hasOwnProperty(targets[0].unit!)) {
if (cloudMonitoringUnitMappings.hasOwnProperty(targets[0].unit!)) {
// @ts-ignore
unit = stackdriverUnitMappings[targets[0].unit];
unit = cloudMonitoringUnitMappings[targets[0].unit];
}
}
return unit;

View File

@ -1,7 +1,7 @@
import _ from 'lodash';
import { alignOptions, aggOptions, ValueTypes, MetricKind, systemLabels } from './constants';
import { SelectableValue } from '@grafana/data';
import StackdriverDatasource from './datasource';
import CloudMonitoringDatasource from './datasource';
import { TemplateSrv } from 'app/features/templating/template_srv';
import { MetricDescriptor, Filter, MetricQuery } from './types';
@ -51,7 +51,7 @@ export const getAggregationOptionsByMetric = (valueType: ValueTypes, metricKind:
};
export const getLabelKeys = async (
datasource: StackdriverDatasource,
datasource: CloudMonitoringDatasource,
selectedMetricType: string,
projectName: string
) => {
@ -107,7 +107,7 @@ export const stringArrayToFilters = (filterArray: string[]) =>
export const toOption = (value: string) => ({ label: value, value } as SelectableValue<string>);
export const formatStackdriverError = (error: any) => {
export const formatCloudMonitoringError = (error: any) => {
let message = error.statusText ?? '';
if (error.data && error.data.error) {
try {

View File

@ -0,0 +1,13 @@
import { DataSourcePlugin } from '@grafana/data';
import CloudMonitoringDatasource from './datasource';
import { QueryEditor } from './components/QueryEditor';
import { CloudMonitoringConfigCtrl } from './config_ctrl';
import { CloudMonitoringAnnotationsQueryCtrl } from './annotations_query_ctrl';
import { CloudMonitoringVariableQueryEditor } from './components/VariableQueryEditor';
import { CloudMonitoringQuery } from './types';
export const plugin = new DataSourcePlugin<CloudMonitoringDatasource, CloudMonitoringQuery>(CloudMonitoringDatasource)
.setQueryEditor(QueryEditor)
.setConfigCtrl(CloudMonitoringConfigCtrl)
.setAnnotationQueryCtrl(CloudMonitoringAnnotationsQueryCtrl)
.setVariableQueryEditor(CloudMonitoringVariableQueryEditor);

View File

@ -1,6 +1,6 @@
<stackdriver-annotation-query-editor
<cloud-monitoring-annotation-query-editor
target="ctrl.annotation.target"
on-query-change="(ctrl.onQueryChange)"
datasource="ctrl.datasource"
template-srv="ctrl.templateSrv"
></stackdriver-annotation-query-editor>
></cloud-monitoring-annotation-query-editor>

View File

@ -1,8 +1,8 @@
<div class="gf-form-group">
<div class="grafana-info-box">
<h4>Stackdriver Authentication</h4>
<h4>Google Cloud Monitoring Authentication</h4>
<p>
There are two ways to authenticate the Stackdriver plugin - either by uploading a Service Account key file, or by
There are two ways to authenticate the Google Cloud Monitoring plugin - either by uploading a Service Account key file, or by
automatically retrieving credentials from the Google metadata server. The latter option is only available when
running Grafana on a GCE virtual machine.
</p>
@ -29,12 +29,12 @@
If Grafana is running on a Google Compute Engine (GCE) virtual machine, it is possible for Grafana to
automatically retrieve the default project id and authentication token from the metadata server. In order for this
to work, you need to make sure that you have a service account that is setup as the default account for the
virtual machine and that the service account has been given read access to the Stackdriver Monitoring API.
virtual machine and that the service account has been given read access to the Google Cloud Monitoring Monitoring API.
</p>
<p>
Detailed instructions on how to create a Service Account can be found
<a class="external-link" target="_blank" href="http://docs.grafana.org/datasources/stackdriver/"
<a class="external-link" target="_blank" href="http://docs.grafana.org/datasources/cloudmonitoring/"
>in the documentation.</a
>
</p>

View File

@ -1,5 +1,5 @@
{
"name": "Stackdriver",
"name": "Google Cloud Monitoring",
"type": "datasource",
"id": "stackdriver",
"category": "cloud",
@ -13,11 +13,11 @@
},
"info": {
"description": "Data source for Google's monitoring service",
"description": "Data source for Google's monitoring service (formerly named Stackdriver)",
"version": "1.0.0",
"logos": {
"small": "img/stackdriver_logo.svg",
"large": "img/stackdriver_logo.svg"
"small": "img/cloud_monitoring_logo.svg",
"large": "img/cloud_monitoring_logo.svg"
},
"author": {
"name": "Grafana Labs",
@ -26,7 +26,7 @@
},
"routes": [
{
"path": "stackdriver",
"path": "cloudmonitoring",
"method": "GET",
"url": "https://content-monitoring.googleapis.com",
"jwtTokenAuth": {

View File

@ -1,8 +1,8 @@
import StackdriverDataSource from '../datasource';
import CloudMonitoringDataSource from '../datasource';
import { metricDescriptors } from './testData';
import { TemplateSrv } from 'app/features/templating/template_srv';
import { DataSourceInstanceSettings, toUtc } from '@grafana/data';
import { StackdriverOptions } from '../types';
import { CloudMonitoringOptions } from '../types';
import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { CustomVariableModel } from '../../../../features/variables/types';
@ -18,12 +18,12 @@ interface Result {
message?: any;
}
describe('StackdriverDataSource', () => {
describe('CloudMonitoringDataSource', () => {
const instanceSettings = ({
jsonData: {
defaultProject: 'testproject',
},
} as unknown) as DataSourceInstanceSettings<StackdriverOptions>;
} as unknown) as DataSourceInstanceSettings<CloudMonitoringOptions>;
const templateSrv = new TemplateSrv();
const timeSrv = {} as TimeSrv;
const datasourceRequestMock = jest.spyOn(backendSrv, 'datasourceRequest');
@ -34,12 +34,12 @@ describe('StackdriverDataSource', () => {
});
describe('when performing testDataSource', () => {
describe('and call to stackdriver api succeeds', () => {
describe('and call to cloud monitoring api succeeds', () => {
let ds;
let result: Result;
beforeEach(async () => {
datasourceRequestMock.mockImplementation(() => Promise.resolve({ status: 200 }));
ds = new StackdriverDataSource(instanceSettings, templateSrv, timeSrv);
ds = new CloudMonitoringDataSource(instanceSettings, templateSrv, timeSrv);
result = await ds.testDatasource();
});
@ -54,7 +54,7 @@ describe('StackdriverDataSource', () => {
beforeEach(async () => {
datasourceRequestMock.mockImplementation(() => Promise.resolve({ status: 200, data: metricDescriptors }));
ds = new StackdriverDataSource(instanceSettings, templateSrv, timeSrv);
ds = new CloudMonitoringDataSource(instanceSettings, templateSrv, timeSrv);
result = await ds.testDatasource();
});
@ -63,7 +63,7 @@ describe('StackdriverDataSource', () => {
});
});
describe('and call to stackdriver api fails with 400 error', () => {
describe('and call to cloud monitoring api fails with 400 error', () => {
let ds;
let result: Result;
beforeEach(async () => {
@ -76,13 +76,15 @@ describe('StackdriverDataSource', () => {
})
);
ds = new StackdriverDataSource(instanceSettings, templateSrv, timeSrv);
ds = new CloudMonitoringDataSource(instanceSettings, templateSrv, timeSrv);
result = await ds.testDatasource();
});
it('should return error status and a detailed error message', () => {
expect(result.status).toEqual('error');
expect(result.message).toBe('Stackdriver: Bad Request: 400. Field interval.endTime had an invalid value');
expect(result.message).toBe(
'Google Cloud Monitoring: Bad Request: 400. Field interval.endTime had an invalid value'
);
});
});
});
@ -105,7 +107,7 @@ describe('StackdriverDataSource', () => {
};
describe('and no time series data is returned', () => {
let ds: StackdriverDataSource;
let ds: CloudMonitoringDataSource;
const response: any = {
results: {
A: {
@ -121,7 +123,7 @@ describe('StackdriverDataSource', () => {
beforeEach(() => {
datasourceRequestMock.mockImplementation(() => Promise.resolve({ status: 200, data: response }));
ds = new StackdriverDataSource(instanceSettings, templateSrv, timeSrv);
ds = new CloudMonitoringDataSource(instanceSettings, templateSrv, timeSrv);
});
it('should return a list of datapoints', () => {
@ -133,7 +135,7 @@ describe('StackdriverDataSource', () => {
});
describe('when performing getMetricTypes', () => {
describe('and call to stackdriver api succeeds', () => {});
describe('and call to cloud monitoring api succeeds', () => {});
let ds;
let result: any;
beforeEach(async () => {
@ -154,7 +156,7 @@ describe('StackdriverDataSource', () => {
})
);
ds = new StackdriverDataSource(instanceSettings, templateSrv, timeSrv);
ds = new CloudMonitoringDataSource(instanceSettings, templateSrv, timeSrv);
// @ts-ignore
result = await ds.getMetricTypes('proj');
});
@ -176,7 +178,7 @@ describe('StackdriverDataSource', () => {
describe('and is single value variable', () => {
beforeEach(() => {
const filterTemplateSrv = initTemplateSrv('filtervalue1');
const ds = new StackdriverDataSource(instanceSettings, filterTemplateSrv, timeSrv);
const ds = new CloudMonitoringDataSource(instanceSettings, filterTemplateSrv, timeSrv);
interpolated = ds.interpolateFilters(['resource.label.zone', '=~', '${test}'], {});
});
@ -189,7 +191,7 @@ describe('StackdriverDataSource', () => {
describe('and is single value variable for the label part', () => {
beforeEach(() => {
const filterTemplateSrv = initTemplateSrv('resource.label.zone');
const ds = new StackdriverDataSource(instanceSettings, filterTemplateSrv, timeSrv);
const ds = new CloudMonitoringDataSource(instanceSettings, filterTemplateSrv, timeSrv);
interpolated = ds.interpolateFilters(['${test}', '=~', 'europe-north-1a'], {});
});
@ -202,7 +204,7 @@ describe('StackdriverDataSource', () => {
describe('and is multi value variable', () => {
beforeEach(() => {
const filterTemplateSrv = initTemplateSrv(['filtervalue1', 'filtervalue2'], true);
const ds = new StackdriverDataSource(instanceSettings, filterTemplateSrv, timeSrv);
const ds = new CloudMonitoringDataSource(instanceSettings, filterTemplateSrv, timeSrv);
interpolated = ds.interpolateFilters(['resource.label.zone', '=~', '[[test]]'], {});
});
@ -218,7 +220,7 @@ describe('StackdriverDataSource', () => {
describe('and is single value variable', () => {
beforeEach(() => {
const groupByTemplateSrv = initTemplateSrv('groupby1');
const ds = new StackdriverDataSource(instanceSettings, groupByTemplateSrv, timeSrv);
const ds = new CloudMonitoringDataSource(instanceSettings, groupByTemplateSrv, timeSrv);
interpolated = ds.interpolateGroupBys(['[[test]]'], {});
});
@ -231,7 +233,7 @@ describe('StackdriverDataSource', () => {
describe('and is multi value variable', () => {
beforeEach(() => {
const groupByTemplateSrv = initTemplateSrv(['groupby1', 'groupby2'], true);
const ds = new StackdriverDataSource(instanceSettings, groupByTemplateSrv, timeSrv);
const ds = new CloudMonitoringDataSource(instanceSettings, groupByTemplateSrv, timeSrv);
interpolated = ds.interpolateGroupBys(['[[test]]'], {});
});
@ -244,12 +246,12 @@ describe('StackdriverDataSource', () => {
});
describe('unit parsing', () => {
let ds: StackdriverDataSource, res: any;
let ds: CloudMonitoringDataSource, res: any;
beforeEach(() => {
ds = new StackdriverDataSource(instanceSettings, templateSrv, timeSrv);
ds = new CloudMonitoringDataSource(instanceSettings, templateSrv, timeSrv);
});
describe('when theres only one target', () => {
describe('and the stackdriver unit doesnt have a corresponding grafana unit', () => {
describe('and the cloud monitoring unit doesnt have a corresponding grafana unit', () => {
beforeEach(() => {
res = ds.resolvePanelUnitFromTargets([{ unit: 'megaseconds' }]);
});
@ -257,7 +259,7 @@ describe('StackdriverDataSource', () => {
expect(res).toBeUndefined();
});
});
describe('and the stackdriver unit has a corresponding grafana unit', () => {
describe('and the cloud monitoring unit has a corresponding grafana unit', () => {
beforeEach(() => {
res = ds.resolvePanelUnitFromTargets([{ unit: 'bit' }]);
});

View File

@ -78,14 +78,14 @@ export interface SLOQuery {
goal?: number;
}
export interface StackdriverQuery extends DataQuery {
export interface CloudMonitoringQuery extends DataQuery {
datasourceId?: number; // Should not be necessary anymore
queryType: QueryType;
metricQuery: MetricQuery;
sloQuery?: SLOQuery;
}
export interface StackdriverOptions extends DataSourceJsonData {
export interface CloudMonitoringOptions extends DataSourceJsonData {
defaultProject?: string;
gceDefaultProject?: string;
authenticationType?: string;

View File

@ -1,7 +0,0 @@
# Stackdriver Data Source - Native Plugin
Grafana ships with built-in support for Google Stackdriver. You just have to add it as a data source and you will be ready to build dashboards for your Stackdriver metrics.
Read more about it here:
[http://docs.grafana.org/datasources/stackdriver/](http://docs.grafana.org/datasources/stackdriver/)

View File

@ -1,13 +0,0 @@
import { DataSourcePlugin } from '@grafana/data';
import StackdriverDatasource from './datasource';
import { QueryEditor } from './components/QueryEditor';
import { StackdriverConfigCtrl } from './config_ctrl';
import { StackdriverAnnotationsQueryCtrl } from './annotations_query_ctrl';
import { StackdriverVariableQueryEditor } from './components/VariableQueryEditor';
import { StackdriverQuery } from './types';
export const plugin = new DataSourcePlugin<StackdriverDatasource, StackdriverQuery>(StackdriverDatasource)
.setQueryEditor(QueryEditor)
.setConfigCtrl(StackdriverConfigCtrl)
.setAnnotationQueryCtrl(StackdriverAnnotationsQueryCtrl)
.setVariableQueryEditor(StackdriverVariableQueryEditor);