Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into release
Browse files Browse the repository at this point in the history
  • Loading branch information
jotak committed Nov 14, 2017
2 parents 4e7b019 + 7165043 commit 635ed99
Show file tree
Hide file tree
Showing 6 changed files with 399 additions and 151 deletions.
25 changes: 25 additions & 0 deletions docker/Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
FROM centos:7

ENV GRAFANA_VERSION 4.1.2-1486989747
ENV GF_PATHS_DATA /var/lib/grafana/data

# Install grafana
RUN yum install -y https://grafanarel.s3.amazonaws.com/builds/grafana-${GRAFANA_VERSION}.x86_64.rpm && \
yum install -y unzip && \
yum clean all

COPY dist /var/lib/grafana/plugins/hawkular-datasource

# Fix permissions so will run on OCP under "restricted" SCC
COPY ./run.sh /run.sh
COPY fix-permissions /usr/bin/fix-permissions

RUN chmod +x /usr/bin/fix-permissions && \
/usr/bin/fix-permissions /run.sh && \
/usr/bin/fix-permissions /var/lib/grafana && \
/usr/bin/fix-permissions /var/log/grafana && \
/usr/bin/fix-permissions /etc/grafana

EXPOSE 3000

ENTRYPOINT ["/run.sh"]
27 changes: 27 additions & 0 deletions docker/instructions-dev-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Building a development Docker image

1. Build the plugin from repository root:

```bash
grunt
```

2. Move (or copy) the `dist` directory to `docker/` and `cd` to it.

```bash
mv dist docker && cd docker
```

3. Build the docker image from Dockerfile.dev

```bash
docker build -f Dockerfile.dev -t hawkular/hawkular-grafana-datasource:dev-build .
```

4. Test the image

```bash
docker run -i -p 3000:3000 --name hawkular-grafana-datasource --rm hawkular/hawkular-grafana-datasource:dev-build
```

And login on http://localhost:3000/
121 changes: 121 additions & 0 deletions spec/datasource-tenant-per-query_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,63 @@ describe('HawkularDatasource tenant per query', () => {
}).then(v => done(), err => done(err));
});

it('should query raw data with templated tenant', done => {

let options = {
range: {
from: 15,
to: 30
},
targets: [{
id: 'memory',
type: 'gauge',
rate: false,
tenant: '$tenant'
}]
};
ctx.templateSrv.variables = [{
name: 'tenant'
}];
ctx.templateSrv.replace = (target, vars) => {
expect(target).to.equal('$tenant');
return '{t1,t2}';
};

let ireq = 1;
ctx.backendSrv.datasourceRequest = request => {
expectRequestWithTenant(request, 'POST', 'gauges/raw/query', 't' + ireq);
expect(request.data).to.deep.equal({
start: options.range.from,
end: options.range.to,
ids: ['memory'],
order: 'ASC'
});
ireq++;

return ctx.$q.when({
status: 200,
data: [{
id: 'memory',
data: [{
timestamp: 13,
value: 3 * ireq
}, {
timestamp: 19,
value: 4 * ireq
}]
}]
});
};

ctx.ds.query(options).then(result => {
expect(result.data).to.have.length(2);
expect(result.data[0].target).to.equal('[t1] memory');
expect(result.data[0].datapoints).to.deep.equal([[6, 13], [8, 19]]);
expect(result.data[1].target).to.equal('[t2] memory');
expect(result.data[1].datapoints).to.deep.equal([[9, 13], [12, 19]]);
}).then(v => done(), err => done(err));
});

it('should query annotations with ad-hoc tenant', done => {

let options = {
Expand Down Expand Up @@ -166,6 +223,70 @@ describe('HawkularDatasource tenant per query', () => {
}).then(v => done(), err => done(err));
});

it('should query stats with templated tenant', done => {
let options = {
range: {
from: 20,
to: 30
},
targets: [{
seriesAggFn: 'none',
stats: ['min','max'],
tags: [{name: 'type', value: 'memory'}],
type: 'gauge',
rate: false,
raw: false,
tenant: '$tenant'
}]
};
ctx.templateSrv.variables = [{
name: 'tenant'
}];
ctx.templateSrv.replace = (target, vars) => {
expect(target).to.equal('$tenant');
return '{t1,t2}';
};

let ireq = 1;
ctx.backendSrv.datasourceRequest = request => {
expectRequestWithTenant(request, 'POST', 'metrics/stats/query', 't' + ireq);
expect(request.data).to.deep.equal({
start: options.range.from,
end: options.range.to,
tags: 'type:memory',
buckets: 60,
types: ['gauge']
});
ireq++;

return ctx.$q.when({
status: 200,
data: {'gauge':
{ 'gauge_1':
[{
start: 20,
end: 25,
min: 3 * ireq,
max: 4 * ireq
}]
}
}
});
};

ctx.ds.query(options).then(result => {
expect(result.data).to.have.length(4);
expect(result.data[0].target).to.equal('[t1] gauge_1 [max]');
expect(result.data[0].datapoints).to.deep.equal([[8, 20]]);
expect(result.data[1].target).to.equal('[t1] gauge_1 [min]');
expect(result.data[1].datapoints).to.deep.equal([[6, 20]]);
expect(result.data[2].target).to.equal('[t2] gauge_1 [max]');
expect(result.data[2].datapoints).to.deep.equal([[12, 20]]);
expect(result.data[3].target).to.equal('[t2] gauge_1 [min]');
expect(result.data[3].datapoints).to.deep.equal([[9, 20]]);
}).then(v => done(), err => done(err));
});

it('should suggest metrics with ad-hoc tenant', done => {
ctx.backendSrv.datasourceRequest = request => {
expectRequestWithTenant(request, 'GET', 'metrics?type=gauge&tags=host=cartago', 'ad-hoc');
Expand Down
108 changes: 81 additions & 27 deletions src/datasource.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ export class HawkularDatasource {
};
this.variablesHelper = new VariablesHelper(templateSrv);
this.capabilitiesPromise = this.queryVersion().then(version => new Capabilities(version));
this.queryProcessor = new QueryProcessor($q, backendSrv, this.variablesHelper, this.capabilitiesPromise, this.metricsUrl,
this.getHeaders.bind(this), this.typeResources);
this.queryProcessor = new QueryProcessor($q, this.multiTenantsQuery.bind(this), this.variablesHelper, this.capabilitiesPromise, this.metricsUrl,
this.typeResources);
}

getHeaders(tenant) {
Expand All @@ -47,6 +47,23 @@ export class HawkularDatasource {
return headers;
}

multiTenantsQuery(tenants, url, params, data, method) {
return this.q.all(tenants.map(tenant => {
return this.backendSrv.datasourceRequest({
url: url,
params: params,
data: data,
method: method,
headers: this.getHeaders(tenant)
}).then(response => {
return {
tenant: tenant,
result: (response.status == 200) ? response.data : null
}
});
}));
}

query(options) {
const validTargets = options.targets
.filter(target => !target.hide)
Expand Down Expand Up @@ -178,48 +195,85 @@ export class HawkularDatasource {
});
}

getTargetTenants(target) {
if (target.tenant) {
return this.variablesHelper.resolve(target.tenant, {});
}
return [null];
}

suggestMetrics(target) {
let url = this.metricsUrl + '/metrics?type=' + target.type;
if (target.tagsQL && target.tagsQL.length > 0) {
url += '&tags=' + this.variablesHelper.resolveForQL(target.tagsQL, {});
} else if (target.tags && target.tags.length > 0) {
url += '&tags=' + tagsModelToString(target.tags, this.variablesHelper, {});
}
return this.backendSrv.datasourceRequest({
url: url,
method: 'GET',
headers: this.getHeaders(target.tenant)
}).then(response => response.status == 200 ? response.data : [])
.then(result => {
return result.map(m => m.id)
.sort()
.map(id => {
return {text: id, value: id};
const tenants = this.getTargetTenants(target);
return this.multiTenantsQuery(tenants, url, null, null, 'GET')
.then(multiTenantsData => {
// Eliminate possible duplicates from multi-tenancy
let ids = {};
multiTenantsData.forEach(tenantData => {
if (tenantData.result) {
tenantData.result.forEach(metric => {
ids[metric.id] = true;
});
}
});
});
return Object.keys(ids)
.sort()
.map(id => {
return {text: id, value: id};
});
});
}

suggestTags(target, key) {
if (!key) {
return this.q.when([]);
}
return this.backendSrv.datasourceRequest({
url: `${this.metricsUrl}/${this.typeResources[target.type]}/tags/${key}:*`,
method: 'GET',
headers: this.getHeaders(target.tenant)
}).then(result => result.data.hasOwnProperty(key) ? result.data[key] : [])
.then(tags => tags.map(tag => {
return {text: tag, value: tag};
}));
const tenants = this.getTargetTenants(target);
const url = `${this.metricsUrl}/${this.typeResources[target.type]}/tags/${key}:*`;
return this.multiTenantsQuery(tenants, url, null, null, 'GET')
.then(multiTenantsData => {
// Eliminate possible duplicates from multi-tenancy
let mergedTags = {};
multiTenantsData.forEach(tenantData => {
if (tenantData.result) {
if (tenantData.result.hasOwnProperty(key)) {
tenantData.result[key].forEach(tag => {
mergedTags[tag] = true;
});
}
}
});
return Object.keys(mergedTags)
.sort()
.map(tag => {
return {text: tag, value: tag};
});
});
}

suggestTagKeys(target) {
return this.backendSrv.datasourceRequest({
url: this.metricsUrl + '/metrics/tags',
method: 'GET',
headers: this.getHeaders(target.tenant)
}).then(response => response.status == 200 ? response.data : [])
.then(result => result.map(key => ({text: key, value: key})));
const tenants = this.getTargetTenants(target);
return this.multiTenantsQuery(tenants, this.metricsUrl + '/metrics/tags', null, null, 'GET')
.then(multiTenantsData => {
// Eliminate possible duplicates from multi-tenancy
let mergedTags = {};
multiTenantsData.forEach(tenantData => {
if (tenantData.result) {
tenantData.result.forEach(tag => {
mergedTags[tag] = true;
});
}
});
return Object.keys(mergedTags)
.map(tag => {
return {text: tag, value: tag};
});
});
}

metricFindQuery(query) {
Expand Down
2 changes: 1 addition & 1 deletion src/partials/query.editor.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div class="gf-form-inline">
<div class="gf-form">
<label ng-if="ctrl.datasource.isTenantPerQuery" class="gf-form-label query-keyword fix-query-keyword">Tenant</label>
<input ng-if="ctrl.datasource.isTenantPerQuery" type="text" class="gf-form-input" ng-model="ctrl.target.tenant" placeholder="default" required>
<input ng-if="ctrl.datasource.isTenantPerQuery" type="text" class="gf-form-input" ng-model="ctrl.target.tenant" ng-change="ctrl.onChangeInternal()" placeholder="default" required>
<select class="gf-form-input" ng-model="ctrl.target.type" ng-change="ctrl.onChangeInternal()">
<option ng-repeat="type in ctrl.metricTypes" value="{{type.value}}">{{type.text}}</option>
</select>
Expand Down
Loading

0 comments on commit 635ed99

Please sign in to comment.