Commit b8231b29 by Daniel Lee

stackdriver: ux for config page, docs updated

parent 65cbcc06
......@@ -12,7 +12,7 @@ weight = 11
# Using Google Stackdriver in Grafana
Grafana ships with built in support for Google Stackdriver. You just have to add it as a datasource and you will be ready to build dashboards for your Stackdriver metrics.
Grafana ships with built-in support for Google Stackdriver. You just have to add it as a datasource and you will be ready to build dashboards for your Stackdriver metrics.
## Adding the data source to Grafana
......@@ -56,11 +56,14 @@ Click on the links above and click the `Enable` 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:
![Create service account key](/img/docs/v54/stackdriver_create_service_account_key.png)
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:
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:
![Choose role](/img/docs/v54/stackdriver_service_account_choose_role.png)
5. Click the Create button. A Json Web Token (JWT) file will be created and downloaded to your computer. Store this file in a secure place as it allows access to your Stackdriver data.
6. Upload it to Grafana on the datasource Configuration page.
6. Upload it to Grafana on the datasource Configuration page. You can either upload the file or paste in the contents of the file.
![Choose role](/img/docs/v54/stackdriver_grafana_upload_key.png)
7. The file contents will be encrypted and saved in the Grafana database. Don't forget to save after uploading the file!
![Choose role](/img/docs/v54/stackdriver_grafana_key_uploaded.png)
## Metric Query Editor
......
......@@ -5,25 +5,20 @@ export class StackdriverConfigCtrl {
jsonText: string;
validationErrors: string[] = [];
inputDataValid: boolean;
defaultProject: string;
projectsError: string;
projects: any[];
loadingProjects: boolean;
/** @ngInject */
constructor(private $scope, datasourceSrv) {
constructor(datasourceSrv) {
this.datasourceSrv = datasourceSrv;
this.current.jsonData = this.current.jsonData || {};
this.current.secureJsonData = this.current.secureJsonData || {};
this.current.secureJsonFields = this.current.secureJsonFields || {};
this.defaultProject = this.current.jsonData.defaultProject;
this.projects = [];
}
save(jwt) {
this.current.secureJsonData.privateKey = jwt.private_key;
this.current.jsonData.tokenUri = jwt.token_uri;
this.current.jsonData.clientEmail = jwt.client_email;
this.current.jsonData.defaultProject = jwt.project_id;
}
validateJwt(jwt) {
......@@ -43,16 +38,15 @@ export class StackdriverConfigCtrl {
if (this.validationErrors.length === 0) {
this.inputDataValid = true;
return true;
} else {
return false;
}
return false;
}
onUpload(json) {
this.jsonText = '';
if (this.validateJwt(json)) {
this.save(json);
this.displayProjects();
}
}
......@@ -61,7 +55,6 @@ export class StackdriverConfigCtrl {
const json = JSON.parse(e.originalEvent.clipboardData.getData('text/plain') || this.jsonText);
if (this.validateJwt(json)) {
this.save(json);
this.displayProjects();
}
} catch (error) {
this.resetValidationMessages();
......@@ -73,44 +66,9 @@ export class StackdriverConfigCtrl {
this.validationErrors = [];
this.inputDataValid = false;
this.jsonText = '';
this.loadingProjects = false;
this.projectsError = '';
this.current.jsonData = {};
this.current.secureJsonData = {};
this.current.secureJsonFields = {};
}
async displayProjects() {
if (this.projects.length === 0) {
try {
this.loadingProjects = true;
const ds = await this.datasourceSrv.loadDatasource(this.current.name);
this.projects = await ds.getProjects();
this.$scope.$apply(() => {
if (this.projects.length > 0) {
this.current.jsonData.defaultProject = this.current.jsonData.defaultProject || this.projects[0].id;
}
});
} catch (error) {
let message = 'Projects cannot be fetched: ';
message += error.statusText ? error.statusText + ': ' : '';
if (error && error.data && error.data.error && error.data.error.message) {
if (error.data.error.code === 403) {
message += `
A list of projects could not be fetched from the Google Cloud Resource Manager API.
You might need to enable it first:
https://console.developers.google.com/apis/library/cloudresourcemanager.googleapis.com`;
} else {
message += error.data.error.code + '. ' + error.data.error.message;
}
} else {
message += 'Cannot connect to Stackdriver API';
}
this.$scope.$apply(() => (this.projectsError = message));
} finally {
this.$scope.$apply(() => (this.loadingProjects = false));
}
}
}
}
<div ng-if="!ctrl.current.jsonData.clientEmail && !ctrl.inputDataValid">
<div class="gf-form-group">
<div class="grafana-info-box">
<h5>GCP Service Account</h5>
<p>
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 datasource integrates with one GCP Project. If you want to
visualize data from multiple GCP Projects then you need to create one datasource per GCP Project.
</p>
<p>
The <strong>Monitoring Viewer</strong> role provides all the permissions that Grafana needs.
</p>
<p>
The following APIs need to be enabled on GCP for the datasource to work:
<ul>
<li><a class="external-link" target="_blank" href="https://console.cloud.google.com/apis/library/monitoring.googleapis.com">Monitoring
API</a></li>
<li><a class="external-link" target="_blank" href="https://console.cloud.google.com/apis/library/cloudresourcemanager.googleapis.com">Resource
Manager API</a></li>
</ul>
</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/">in
the documentation.</a></p>
</div>
</div>
<div class="gf-form-group">
<div class="gf-form">
<h3>Service Account Authentication</h3>
<info-popover mode="header">Upload your Service Account key file or paste in the contents of the file. The file
contents will be encrypted and saved in the Grafana database.</info-popover>
</div>
<div ng-if="!ctrl.current.jsonData.clientEmail && !ctrl.inputDataValid">
<div class="gf-form-group" ng-if="!ctrl.inputDataValid">
<div class="gf-form">
<form>
<dash-upload on-upload="ctrl.onUpload(dash)"></dash-upload>
<dash-upload on-upload="ctrl.onUpload(dash)" btn-text="Upload Service Account key file"></dash-upload>
</form>
</div>
</div>
<div class="gf-form-group">
<h5 class="section-heading" ng-if="!ctrl.inputDataValid">Or paste JSON</h5>
<h5 class="section-heading" ng-if="!ctrl.inputDataValid">Or paste Service Account key JSON</h5>
<div class="gf-form" ng-if="!ctrl.inputDataValid">
<textarea rows="10" data-share-panel-url="" class="gf-form-input" ng-model="ctrl.jsonText" ng-paste="ctrl.onPasteJwt($event)"></textarea>
</div>
......@@ -16,44 +49,36 @@
{{valError}}
</div>
</div>
</div>
</div>
<div class="gf-form-group" ng-if="ctrl.inputDataValid || ctrl.current.jsonData.clientEmail">
<div class="gf-form-inline">
<h6>Uploaded Key Details</h6>
<div class="gf-form">
<span class="gf-form-label width-9">Token URI</span>
<input class="gf-form-input width-30" disabled type="text" ng-model='ctrl.current.jsonData.tokenUri' />
<span class="gf-form-label width-9">Project</span>
<input class="gf-form-input width-40" disabled type="text" ng-model="ctrl.current.jsonData.defaultProject" />
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form">
<span class="gf-form-label width-9">Client Email</span>
<input class="gf-form-input width-30" disabled type="text" ng-model="ctrl.current.jsonData.clientEmail" />
<input class="gf-form-input width-40" disabled type="text" ng-model="ctrl.current.jsonData.clientEmail" />
</div>
<div class="gf-form">
<span class="gf-form-label width-9">Token URI</span>
<input class="gf-form-input width-40" disabled type="text" ng-model='ctrl.current.jsonData.tokenUri' />
</div>
<div class="gf-form" ng-if="ctrl.current.secureJsonFields.privateKey">
<span class="gf-form-label width-9">Private Key</span>
<input type="text" class="gf-form-input max-width-12" disabled="disabled" value="configured">
</div>
<div class="gf-form">
<a class="btn btn-secondary gf-form-btn" href="#" ng-click="ctrl.resetValidationMessages()">Reset form</a>
</div>
<br />
<div class="gf-form">
<span class="gf-form-label width-10">Default Project</span>
<div class="gf-form-select-wrapper max-width-23">
<select class="gf-form-input" ng-model="ctrl.current.jsonData.defaultProject" ng-options="p.id as p.name for p in ctrl.projects"
ng-change="ctrl.userChangedDefaultProject()"></select>
</div>
<div ng-if="ctrl.loadingProjects">
<i class="fa fa-spinner fa-spin"></i>
<em>Fetching projects...&hellip;</em>
</div>
</div>
<div ng-if="ctrl.projectsError" class="text-error p-l-1">
<i class="fa fa-warning"></i>
{{ctrl.projectsError}}
<div class="gf-form width-18">
<a class="btn btn-secondary gf-form-btn" href="#" ng-click="ctrl.resetValidationMessages()">Reset Service
Account Key </a>
<info-popover mode="right-normal">
Reset to clear the uploaded key and upload a new file.
</info-popover>
</div>
</div>
<p class="gf-form-label" ng-hide="ctrl.current.secureJsonFields.privateKey"><i class="fa fa-save"></i> Do not forget to save your changes after uploading a file.</p>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment