-
-
-
Column Styles
-
-
-
diff --git a/public/app/plugins/panel/table/module.ts b/public/app/plugins/panel/table/module.ts
index 86847a01aac..d8d174c5c84 100644
--- a/public/app/plugins/panel/table/module.ts
+++ b/public/app/plugins/panel/table/module.ts
@@ -78,7 +78,8 @@ class TablePanelCtrl extends MetricsPanelCtrl {
if (this.panel.transform === 'annotations') {
this.setTimeQueryStart();
- return this.annotationsSrv.getAnnotations(this.dashboard).then(annotations => {
+ return this.annotationsSrv.getAnnotations({dashboard: this.dashboard, panel: this.panel, range: this.range})
+ .then(annotations => {
return {data: annotations};
});
}
diff --git a/public/app/plugins/panel/table/options.html b/public/app/plugins/panel/table/options.html
deleted file mode 100644
index d43ff958c5d..00000000000
--- a/public/app/plugins/panel/table/options.html
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/public/sass/components/_gf-form.scss b/public/sass/components/_gf-form.scss
index 82ca9073a4a..b1df60426d7 100644
--- a/public/sass/components/_gf-form.scss
+++ b/public/sass/components/_gf-form.scss
@@ -7,6 +7,7 @@ $gf-form-margin: 0.25rem;
align-items: center;
text-align: left;
position: relative;
+ font-size: $font-size-sm;
&--offset-1 {
margin-left: $spacer;
@@ -48,7 +49,6 @@ $gf-form-margin: 0.25rem;
.gf-form-label {
padding: $input-padding-y $input-padding-x;
margin-right: $gf-form-margin;
- line-height: $input-line-height;
flex-shrink: 0;
background-color: $input-label-bg;
@@ -135,7 +135,7 @@ $gf-form-margin: 0.25rem;
&::after {
position: absolute;
top: 35%;
- right: $input-padding-x/2;
+ right: $input-padding-x;
background-color: transparent;
color: $input-color;
font: normal normal normal $font-size-sm/1 FontAwesome;
diff --git a/public/sass/components/_search.scss b/public/sass/components/_search.scss
index 563f89919e5..2eeb34ec0b4 100644
--- a/public/sass/components/_search.scss
+++ b/public/sass/components/_search.scss
@@ -111,7 +111,7 @@
font-size: $font-size-sm;
padding-right: 7rem;
background: url(../img/grafana_net_logo.svg);
- background-size: 6.5rem 3rem;
+ background-size: 6.5rem;
background-repeat: no-repeat;
background-position: right;
position: relative;
diff --git a/public/sass/components/_submenu.scss b/public/sass/components/_submenu.scss
index 5d1d0648dda..14f0658f7cb 100644
--- a/public/sass/components/_submenu.scss
+++ b/public/sass/components/_submenu.scss
@@ -1,6 +1,5 @@
.submenu-controls {
margin: 0 $panel-margin ($panel-margin*2) $panel-margin;
- font-size: 16px;
}
.annotation-disabled, .annotation-disabled a {
@@ -18,22 +17,19 @@
.submenu-item {
margin-right: 20px;
display: inline-block;
- border-radius: 3px;
- background-color: $panel-bg;
- border: $panel-border;
- margin-right: 10px;
+ margin-right: 15px;
display: inline-block;
float: left;
.fa-caret-down {
font-size: 75%;
position: relative;
- top: 1px;
+ top: -1px;
+ left: 1px;
}
}
.variable-value-link {
- font-size: 16px;
padding-right: 10px;
.label-tag {
margin: 0 5px;
@@ -42,19 +38,9 @@
padding: 8px 7px;
box-sizing: content-box;
display: inline-block;
- font-weight: normal;
- display: inline-block;
color: $text-color;
}
-.submenu-item-label {
- padding: 8px 0px 8px 7px;
- box-sizing: content-box;
- display: inline-block;
- font-weight: normal;
- display: inline-block;
-}
-
.variable-link-wrapper {
display: inline-block;
position: relative;
diff --git a/public/sass/pages/_alerting.scss b/public/sass/pages/_alerting.scss
index 33577d1e30a..79eaa8ad8ee 100644
--- a/public/sass/pages/_alerting.scss
+++ b/public/sass/pages/_alerting.scss
@@ -38,3 +38,33 @@
top: 2px;
}
}
+
+.panel-has-alert {
+ .panel-alert-icon:before {
+ content: "\e611";
+ position: relative;
+ top: 1px;
+ left: -3px;
+ }
+}
+
+.panel-alert-state {
+ &--alerting {
+ box-shadow: 0 0 10px $critical;
+
+ .panel-alert-icon:before {
+ color: $critical;
+ content: "\e610";
+ }
+ }
+
+ &--ok {
+ //box-shadow: 0 0 5px rgba(0,200,0,10.8);
+ .panel-alert-icon:before {
+ color: $online;
+ content: "\e611";
+ }
+ }
+}
+
+
diff --git a/public/sass/pages/_login.scss b/public/sass/pages/_login.scss
index 8f376dfae71..8641aabe1b1 100644
--- a/public/sass/pages/_login.scss
+++ b/public/sass/pages/_login.scss
@@ -112,6 +112,19 @@
background: #555;
color: white;
}
+
+ .btn-grafana-net {
+ background: url(../img/grafana_net_logo.svg);
+ background-size: 10rem;
+ background-repeat: no-repeat;
+ background-position: right 35%;
+ overflow: hidden;
+ padding-right: 10.5rem;
+
+ span {
+ display: none;
+ }
+ }
}
.password-recovery {
@@ -157,7 +170,7 @@
.invite-box {
text-align: center;
border: 1px solid $tight-form-func-bg;
- background-color: $panel-bg;
+ background-color: $panel-bg;
max-width: 800px;
margin-left: auto;
margin-right: auto;
diff --git a/public/test/core/time_series_specs.js b/public/test/core/time_series_specs.js
index 034e872e2f1..2b325cf6d46 100644
--- a/public/test/core/time_series_specs.js
+++ b/public/test/core/time_series_specs.js
@@ -56,6 +56,38 @@ define([
});
});
+ describe('When checking if ms resolution is needed', function() {
+ describe('msResolution with second resolution timestamps', function() {
+ beforeEach(function() {
+ series = new TimeSeries({datapoints: [[45, 1234567890], [60, 1234567899]]});
+ });
+
+ it('should set hasMsResolution to false', function() {
+ expect(series.hasMsResolution).to.be(false);
+ });
+ });
+
+ describe('msResolution with millisecond resolution timestamps', function() {
+ beforeEach(function() {
+ series = new TimeSeries({datapoints: [[55, 1236547890001], [90, 1234456709000]]});
+ });
+
+ it('should show millisecond resolution tooltip', function() {
+ expect(series.hasMsResolution).to.be(true);
+ });
+ });
+
+ describe('msResolution with millisecond resolution timestamps but with trailing zeroes', function() {
+ beforeEach(function() {
+ series = new TimeSeries({datapoints: [[45, 1234567890000], [60, 1234567899000]]});
+ });
+
+ it('should not show millisecond resolution tooltip', function() {
+ expect(series.hasMsResolution).to.be(false);
+ });
+ });
+ });
+
describe('can detect if series contains ms precision', function() {
var fakedata;
diff --git a/public/test/core/utils/emitter_specs.ts b/public/test/core/utils/emitter_specs.ts
index fec4d02a649..388d3849055 100644
--- a/public/test/core/utils/emitter_specs.ts
+++ b/public/test/core/utils/emitter_specs.ts
@@ -24,6 +24,22 @@ describe("Emitter", () => {
expect(sub2Called).to.be(true);
});
+ it('when subscribing twice', () => {
+ var events = new Emitter();
+ var sub1Called = 0;
+
+ function handler() {
+ sub1Called += 1;
+ }
+
+ events.on('test', handler);
+ events.on('test', handler);
+
+ events.emit('test', null);
+
+ expect(sub1Called).to.be(2);
+ });
+
it('should handle errors', () => {
var events = new Emitter();
var sub1Called = 0;
diff --git a/public/test/core/utils/kbn_specs.js b/public/test/core/utils/kbn_specs.js
index 959b176b06c..95bef57ef1a 100644
--- a/public/test/core/utils/kbn_specs.js
+++ b/public/test/core/utils/kbn_specs.js
@@ -132,62 +132,64 @@ define([
describe('calculateInterval', function() {
it('1h 100 resultion', function() {
var range = { from: dateMath.parse('now-1h'), to: dateMath.parse('now') };
- var str = kbn.calculateInterval(range, 100, null);
- expect(str).to.be('30s');
+ var res = kbn.calculateInterval(range, 100, null);
+ expect(res.interval).to.be('30s');
});
it('10m 1600 resolution', function() {
var range = { from: dateMath.parse('now-10m'), to: dateMath.parse('now') };
- var str = kbn.calculateInterval(range, 1600, null);
- expect(str).to.be('500ms');
+ var res = kbn.calculateInterval(range, 1600, null);
+ expect(res.interval).to.be('500ms');
+ expect(res.intervalMs).to.be(500);
});
it('fixed user interval', function() {
var range = { from: dateMath.parse('now-10m'), to: dateMath.parse('now') };
- var str = kbn.calculateInterval(range, 1600, '10s');
- expect(str).to.be('10s');
+ var res = kbn.calculateInterval(range, 1600, '10s');
+ expect(res.interval).to.be('10s');
+ expect(res.intervalMs).to.be(10000);
});
it('short time range and user low limit', function() {
var range = { from: dateMath.parse('now-10m'), to: dateMath.parse('now') };
- var str = kbn.calculateInterval(range, 1600, '>10s');
- expect(str).to.be('10s');
+ var res = kbn.calculateInterval(range, 1600, '>10s');
+ expect(res.interval).to.be('10s');
});
it('large time range and user low limit', function() {
- var range = { from: dateMath.parse('now-14d'), to: dateMath.parse('now') };
- var str = kbn.calculateInterval(range, 1000, '>10s');
- expect(str).to.be('20m');
+ var range = {from: dateMath.parse('now-14d'), to: dateMath.parse('now')};
+ var res = kbn.calculateInterval(range, 1000, '>10s');
+ expect(res.interval).to.be('20m');
});
-
+
it('10s 900 resolution and user low limit in ms', function() {
var range = { from: dateMath.parse('now-10s'), to: dateMath.parse('now') };
- var str = kbn.calculateInterval(range, 900, '>15ms');
- expect(str).to.be('15ms');
+ var res = kbn.calculateInterval(range, 900, '>15ms');
+ expect(res.interval).to.be('15ms');
});
});
describe('hex', function() {
- it('positive integer', function() {
- var str = kbn.valueFormats.hex(100, 0);
- expect(str).to.be('64');
- });
- it('negative integer', function() {
- var str = kbn.valueFormats.hex(-100, 0);
- expect(str).to.be('-64');
- });
- it('null', function() {
- var str = kbn.valueFormats.hex(null, 0);
- expect(str).to.be('');
- });
- it('positive float', function() {
- var str = kbn.valueFormats.hex(50.52, 1);
- expect(str).to.be('32.8');
- });
- it('negative float', function() {
- var str = kbn.valueFormats.hex(-50.333, 2);
- expect(str).to.be('-32.547AE147AE14');
- });
+ it('positive integer', function() {
+ var str = kbn.valueFormats.hex(100, 0);
+ expect(str).to.be('64');
+ });
+ it('negative integer', function() {
+ var str = kbn.valueFormats.hex(-100, 0);
+ expect(str).to.be('-64');
+ });
+ it('null', function() {
+ var str = kbn.valueFormats.hex(null, 0);
+ expect(str).to.be('');
+ });
+ it('positive float', function() {
+ var str = kbn.valueFormats.hex(50.52, 1);
+ expect(str).to.be('32.8');
+ });
+ it('negative float', function() {
+ var str = kbn.valueFormats.hex(-50.333, 2);
+ expect(str).to.be('-32.547AE147AE14');
+ });
});
describe('hex 0x', function() {
diff --git a/public/test/specs/dashboardSrv-specs.js b/public/test/specs/dashboardSrv-specs.js
deleted file mode 100644
index 65441bb3b41..00000000000
--- a/public/test/specs/dashboardSrv-specs.js
+++ /dev/null
@@ -1,388 +0,0 @@
-define([
- 'app/features/dashboard/dashboardSrv'
-], function() {
- 'use strict';
-
- describe('dashboardSrv', function() {
- var _dashboardSrv;
-
- beforeEach(module('grafana.services'));
- beforeEach(module(function($provide) {
- $provide.value('contextSrv', {
- });
- }));
-
- beforeEach(inject(function(dashboardSrv) {
- _dashboardSrv = dashboardSrv;
- }));
-
- describe('when creating new dashboard with defaults only', function() {
- var model;
-
- beforeEach(function() {
- model = _dashboardSrv.create({}, {});
- });
-
- it('should have title', function() {
- expect(model.title).to.be('No Title');
- });
-
- it('should have meta', function() {
- expect(model.meta.canSave).to.be(true);
- expect(model.meta.canShare).to.be(true);
- });
-
- it('should have default properties', function() {
- expect(model.rows.length).to.be(0);
- });
- });
-
- describe('when getting next panel id', function() {
- var model;
-
- beforeEach(function() {
- model = _dashboardSrv.create({
- rows: [{ panels: [{ id: 5 }]}]
- });
- });
-
- it('should return max id + 1', function() {
- expect(model.getNextPanelId()).to.be(6);
- });
- });
-
- describe('row and panel manipulation', function() {
- var dashboard;
-
- beforeEach(function() {
- dashboard = _dashboardSrv.create({});
- });
-
- it('row span should sum spans', function() {
- var spanLeft = dashboard.rowSpan({ panels: [{ span: 2 }, { span: 3 }] });
- expect(spanLeft).to.be(5);
- });
-
- it('adding default should split span in half', function() {
- dashboard.rows = [{ panels: [{ span: 12, id: 7 }] }];
- dashboard.addPanel({span: 4}, dashboard.rows[0]);
-
- expect(dashboard.rows[0].panels[0].span).to.be(6);
- expect(dashboard.rows[0].panels[1].span).to.be(6);
- expect(dashboard.rows[0].panels[1].id).to.be(8);
- });
-
- it('duplicate panel should try to add it to same row', function() {
- var panel = { span: 4, attr: '123', id: 10 };
- dashboard.rows = [{ panels: [panel] }];
- dashboard.duplicatePanel(panel, dashboard.rows[0]);
-
- expect(dashboard.rows[0].panels[0].span).to.be(4);
- expect(dashboard.rows[0].panels[1].span).to.be(4);
- expect(dashboard.rows[0].panels[1].attr).to.be('123');
- expect(dashboard.rows[0].panels[1].id).to.be(11);
- });
-
- it('duplicate panel should remove repeat data', function() {
- var panel = { span: 4, attr: '123', id: 10, repeat: 'asd', scopedVars: { test: 'asd' }};
- dashboard.rows = [{ panels: [panel] }];
- dashboard.duplicatePanel(panel, dashboard.rows[0]);
-
- expect(dashboard.rows[0].panels[1].repeat).to.be(undefined);
- expect(dashboard.rows[0].panels[1].scopedVars).to.be(undefined);
- });
-
- });
-
- describe('when creating dashboard with editable false', function() {
- var model;
-
- beforeEach(function() {
- model = _dashboardSrv.create({
- editable: false
- });
- });
-
- it('should set editable false', function() {
- expect(model.editable).to.be(false);
- });
-
- });
-
- describe('when creating dashboard with old schema', function() {
- var model;
- var graph;
- var singlestat;
- var table;
-
- beforeEach(function() {
- model = _dashboardSrv.create({
- services: { filter: { time: { from: 'now-1d', to: 'now'}, list: [{}] }},
- pulldowns: [
- {type: 'filtering', enable: true},
- {type: 'annotations', enable: true, annotations: [{name: 'old'}]}
- ],
- rows: [
- {
- panels: [
- {
- type: 'graph', legend: true, aliasYAxis: { test: 2 },
- y_formats: ['kbyte', 'ms'],
- grid: {
- min: 1,
- max: 10,
- rightMin: 5,
- rightMax: 15,
- leftLogBase: 1,
- rightLogBase: 2,
- threshold1: 200,
- threshold2: 400,
- threshold1Color: 'yellow',
- threshold2Color: 'red',
- },
- leftYAxisLabel: 'left label',
- targets: [{refId: 'A'}, {}],
- },
- {
- type: 'singlestat', legend: true, thresholds: '10,20,30', aliasYAxis: { test: 2 }, grid: { min: 1, max: 10 },
- targets: [{refId: 'A'}, {}],
- },
- {
- type: 'table', legend: true, styles: [{ thresholds: ["10", "20", "30"]}, { thresholds: ["100", "200", "300"]}],
- targets: [{refId: 'A'}, {}],
- }
- ]
- }
- ]
- });
-
- graph = model.rows[0].panels[0];
- singlestat = model.rows[0].panels[1];
- table = model.rows[0].panels[2];
- });
-
- it('should have title', function() {
- expect(model.title).to.be('No Title');
- });
-
- it('should have panel id', function() {
- expect(graph.id).to.be(1);
- });
-
- it('should move time and filtering list', function() {
- expect(model.time.from).to.be('now-1d');
- expect(model.templating.list[0].allFormat).to.be('glob');
- });
-
- it('graphite panel should change name too graph', function() {
- expect(graph.type).to.be('graph');
- });
-
- it('single stat panel should have two thresholds', function() {
- expect(singlestat.thresholds).to.be('20,30');
- });
-
- it('queries without refId should get it', function() {
- expect(graph.targets[1].refId).to.be('B');
- });
-
- it('update legend setting', function() {
- expect(graph.legend.show).to.be(true);
- });
-
- it('move aliasYAxis to series override', function() {
- expect(graph.seriesOverrides[0].alias).to.be("test");
- expect(graph.seriesOverrides[0].yaxis).to.be(2);
- });
-
- it('should move pulldowns to new schema', function() {
- expect(model.annotations.list[0].name).to.be('old');
- });
-
- it('table panel should only have two thresholds values', function() {
- expect(table.styles[0].thresholds[0]).to.be("20");
- expect(table.styles[0].thresholds[1]).to.be("30");
- expect(table.styles[1].thresholds[0]).to.be("200");
- expect(table.styles[1].thresholds[1]).to.be("300");
- });
-
- it('graph grid to yaxes options', function() {
- expect(graph.yaxes[0].min).to.be(1);
- expect(graph.yaxes[0].max).to.be(10);
- expect(graph.yaxes[0].format).to.be('kbyte');
- expect(graph.yaxes[0].label).to.be('left label');
- expect(graph.yaxes[0].logBase).to.be(1);
- expect(graph.yaxes[1].min).to.be(5);
- expect(graph.yaxes[1].max).to.be(15);
- expect(graph.yaxes[1].format).to.be('ms');
- expect(graph.yaxes[1].logBase).to.be(2);
-
- expect(graph.grid.rightMax).to.be(undefined);
- expect(graph.grid.rightLogBase).to.be(undefined);
- expect(graph.y_formats).to.be(undefined);
- });
-
- it('dashboard schema version should be set to latest', function() {
- expect(model.schemaVersion).to.be(13);
- });
-
- it('graph thresholds should be migrated', function() {
- expect(graph.thresholds.length).to.be(2);
- expect(graph.thresholds[0].op).to.be('>');
- expect(graph.thresholds[0].value).to.be(400);
- expect(graph.thresholds[0].fillColor).to.be('red');
- expect(graph.thresholds[1].value).to.be(200);
- expect(graph.thresholds[1].fillColor).to.be('yellow');
- });
- });
-
- describe('when creating dashboard model with missing list for annoations or templating', function() {
- var model;
-
- beforeEach(function() {
- model = _dashboardSrv.create({
- annotations: {
- enable: true,
- },
- templating: {
- enable: true
- }
- });
- });
-
- it('should add empty list', function() {
- expect(model.annotations.list.length).to.be(0);
- expect(model.templating.list.length).to.be(0);
- });
- });
-
- describe('Given editable false dashboard', function() {
- var model;
-
- beforeEach(function() {
- model = _dashboardSrv.create({
- editable: false,
- });
- });
-
- it('Should set meta canEdit and canSave to false', function() {
- expect(model.meta.canSave).to.be(false);
- expect(model.meta.canEdit).to.be(false);
- });
-
- it('getSaveModelClone should remove meta', function() {
- var clone = model.getSaveModelClone();
- expect(clone.meta).to.be(undefined);
- });
- });
-
- describe('when loading dashboard with old influxdb query schema', function() {
- var model;
- var target;
-
- beforeEach(function() {
- model = _dashboardSrv.create({
- rows: [{
- panels: [{
- type: 'graph',
- grid: {},
- yaxes: [{}, {}],
- targets: [{
- "alias": "$tag_datacenter $tag_source $col",
- "column": "value",
- "measurement": "logins.count",
- "fields": [
- {
- "func": "mean",
- "name": "value",
- "mathExpr": "*2",
- "asExpr": "value"
- },
- {
- "name": "one-minute",
- "func": "mean",
- "mathExpr": "*3",
- "asExpr": "one-minute"
- }
- ],
- "tags": [],
- "fill": "previous",
- "function": "mean",
- "groupBy": [
- {
- "interval": "auto",
- "type": "time"
- },
- {
- "key": "source",
- "type": "tag"
- },
- {
- "type": "tag",
- "key": "datacenter"
- }
- ],
- }]
- }]
- }]
- });
-
- target = model.rows[0].panels[0].targets[0];
- });
-
- it('should update query schema', function() {
- expect(target.fields).to.be(undefined);
- expect(target.select.length).to.be(2);
- expect(target.select[0].length).to.be(4);
- expect(target.select[0][0].type).to.be('field');
- expect(target.select[0][1].type).to.be('mean');
- expect(target.select[0][2].type).to.be('math');
- expect(target.select[0][3].type).to.be('alias');
- });
-
- });
-
- describe('when creating dashboard model with missing list for annoations or templating', function() {
- var model;
-
- beforeEach(function() {
- model = _dashboardSrv.create({
- annotations: {
- enable: true,
- },
- templating: {
- enable: true
- }
- });
- });
-
- it('should add empty list', function() {
- expect(model.annotations.list.length).to.be(0);
- expect(model.templating.list.length).to.be(0);
- });
- });
-
- describe('Formatting epoch timestamp when timezone is set as utc', function() {
- var dashboard;
-
- beforeEach(function() {
- dashboard = _dashboardSrv.create({
- timezone: 'utc',
- });
- });
-
- it('Should format timestamp with second resolution by default', function() {
- expect(dashboard.formatDate(1234567890000)).to.be('2009-02-13 23:31:30');
- });
-
- it('Should format timestamp with second resolution even if second format is passed as parameter', function() {
- expect(dashboard.formatDate(1234567890007,'YYYY-MM-DD HH:mm:ss')).to.be('2009-02-13 23:31:30');
- });
-
- it('Should format timestamp with millisecond resolution if format is passed as parameter', function() {
- expect(dashboard.formatDate(1234567890007,'YYYY-MM-DD HH:mm:ss.SSS')).to.be('2009-02-13 23:31:30.007');
- });
- });
-
- });
-});
diff --git a/public/test/specs/helpers.js b/public/test/specs/helpers.js
index 0e5f20a853c..424b190b6dd 100644
--- a/public/test/specs/helpers.js
+++ b/public/test/specs/helpers.js
@@ -158,6 +158,7 @@ define([
return _.template(text, this.templateSettings)(this.data);
};
this.init = function() {};
+ this.getAdhocFilters = function() { return []; };
this.fillVariableValuesForUrl = function() {};
this.updateTemplateData = function() { };
this.variableExists = function() { return false; };
diff --git a/public/test/specs/templateSrv-specs.js b/public/test/specs/templateSrv-specs.js
deleted file mode 100644
index a592cc7a152..00000000000
--- a/public/test/specs/templateSrv-specs.js
+++ /dev/null
@@ -1,267 +0,0 @@
-define([
- '../mocks/dashboard-mock',
- 'lodash',
- 'app/features/templating/templateSrv'
-], function(dashboardMock) {
- 'use strict';
-
- describe('templateSrv', function() {
- var _templateSrv;
- var _dashboard;
-
- beforeEach(module('grafana.services'));
- beforeEach(module(function() {
- _dashboard = dashboardMock.create();
- }));
-
- beforeEach(inject(function(templateSrv) {
- _templateSrv = templateSrv;
- }));
-
- describe('init', function() {
- beforeEach(function() {
- _templateSrv.init([{ name: 'test', current: { value: 'oogle' } }]);
- });
-
- it('should initialize template data', function() {
- var target = _templateSrv.replace('this.[[test]].filters');
- expect(target).to.be('this.oogle.filters');
- });
- });
-
- describe('replace can pass scoped vars', function() {
- beforeEach(function() {
- _templateSrv.init([{ name: 'test', current: { value: 'oogle' } }]);
- });
-
- it('should replace $test with scoped value', function() {
- var target = _templateSrv.replace('this.$test.filters', {'test': {value: 'mupp', text: 'asd'}});
- expect(target).to.be('this.mupp.filters');
- });
-
- it('should replace $test with scoped text', function() {
- var target = _templateSrv.replaceWithText('this.$test.filters', {'test': {value: 'mupp', text: 'asd'}});
- expect(target).to.be('this.asd.filters');
- });
- });
-
- describe('replace can pass multi / all format', function() {
- beforeEach(function() {
- _templateSrv.init([{name: 'test', current: {value: ['value1', 'value2'] }}]);
- });
-
- it('should replace $test with globbed value', function() {
- var target = _templateSrv.replace('this.$test.filters', {}, 'glob');
- expect(target).to.be('this.{value1,value2}.filters');
- });
-
- it('should replace $test with piped value', function() {
- var target = _templateSrv.replace('this=$test', {}, 'pipe');
- expect(target).to.be('this=value1|value2');
- });
-
- it('should replace $test with piped value', function() {
- var target = _templateSrv.replace('this=$test', {}, 'pipe');
- expect(target).to.be('this=value1|value2');
- });
- });
-
- describe('variable with all option', function() {
- beforeEach(function() {
- _templateSrv.init([{
- name: 'test',
- current: {value: '$__all' },
- options: [
- {value: '$__all'}, {value: 'value1'}, {value: 'value2'}
- ]
- }]);
- });
-
- it('should replace $test with formatted all value', function() {
- var target = _templateSrv.replace('this.$test.filters', {}, 'glob');
- expect(target).to.be('this.{value1,value2}.filters');
- });
- });
-
- describe('variable with all option and custom value', function() {
- beforeEach(function() {
- _templateSrv.init([{
- name: 'test',
- current: {value: '$__all' },
- allValue: '*',
- options: [
- {value: 'value1'}, {value: 'value2'}
- ]
- }]);
- });
-
- it('should replace $test with formatted all value', function() {
- var target = _templateSrv.replace('this.$test.filters', {}, 'glob');
- expect(target).to.be('this.*.filters');
- });
-
- it('should not escape custom all value', function() {
- var target = _templateSrv.replace('this.$test', {}, 'regex');
- expect(target).to.be('this.*');
- });
- });
-
- describe('lucene format', function() {
- it('should properly escape $test with lucene escape sequences', function() {
- _templateSrv.init([{name: 'test', current: {value: 'value/4' }}]);
- var target = _templateSrv.replace('this:$test', {}, 'lucene');
- expect(target).to.be("this:value\\\/4");
- });
- });
-
- describe('format variable to string values', function() {
- it('single value should return value', function() {
- var result = _templateSrv.formatValue('test');
- expect(result).to.be('test');
- });
-
- it('multi value and glob format should render glob string', function() {
- var result = _templateSrv.formatValue(['test','test2'], 'glob');
- expect(result).to.be('{test,test2}');
- });
-
- it('multi value and lucene should render as lucene expr', function() {
- var result = _templateSrv.formatValue(['test','test2'], 'lucene');
- expect(result).to.be('("test" OR "test2")');
- });
-
- it('multi value and regex format should render regex string', function() {
- var result = _templateSrv.formatValue(['test.','test2'], 'regex');
- expect(result).to.be('(test\\.|test2)');
- });
-
- it('multi value and pipe should render pipe string', function() {
- var result = _templateSrv.formatValue(['test','test2'], 'pipe');
- expect(result).to.be('test|test2');
- });
-
- it('slash should be properly escaped in regex format', function() {
- var result = _templateSrv.formatValue('Gi3/14', 'regex');
- expect(result).to.be('Gi3\\/14');
- });
-
- });
-
- describe('can check if variable exists', function() {
- beforeEach(function() {
- _templateSrv.init([{ name: 'test', current: { value: 'oogle' } }]);
- });
-
- it('should return true if exists', function() {
- var result = _templateSrv.variableExists('$test');
- expect(result).to.be(true);
- });
- });
-
- describe('can hightlight variables in string', function() {
- beforeEach(function() {
- _templateSrv.init([{ name: 'test', current: { value: 'oogle' } }]);
- });
-
- it('should insert html', function() {
- var result = _templateSrv.highlightVariablesAsHtml('$test');
- expect(result).to.be('
$test');
- });
-
- it('should insert html anywhere in string', function() {
- var result = _templateSrv.highlightVariablesAsHtml('this $test ok');
- expect(result).to.be('this
$test ok');
- });
-
- it('should ignore if variables does not exist', function() {
- var result = _templateSrv.highlightVariablesAsHtml('this $google ok');
- expect(result).to.be('this $google ok');
- });
-
- });
-
- describe('when checking if a string contains a variable', function() {
- beforeEach(function() {
- _templateSrv.init([{ name: 'test', current: { value: 'muuuu' } }]);
- });
-
- it('should find it with $var syntax', function() {
- var contains = _templateSrv.containsVariable('this.$test.filters', 'test');
- expect(contains).to.be(true);
- });
-
- it('should not find it if only part matches with $var syntax', function() {
- var contains = _templateSrv.containsVariable('this.$ServerDomain.filters', 'Server');
- expect(contains).to.be(false);
- });
-
- it('should find it with [[var]] syntax', function() {
- var contains = _templateSrv.containsVariable('this.[[test]].filters', 'test');
- expect(contains).to.be(true);
- });
-
- it('should find it when part of segment', function() {
- var contains = _templateSrv.containsVariable('metrics.$env.$group-*', 'group');
- expect(contains).to.be(true);
- });
-
- it('should find it its the only thing', function() {
- var contains = _templateSrv.containsVariable('$env', 'env');
- expect(contains).to.be(true);
- });
- });
-
- describe('updateTemplateData with simple value', function() {
- beforeEach(function() {
- _templateSrv.init([{ name: 'test', current: { value: 'muuuu' } }]);
- });
-
- it('should set current value and update template data', function() {
- var target = _templateSrv.replace('this.[[test]].filters');
- expect(target).to.be('this.muuuu.filters');
- });
- });
-
- describe('fillVariableValuesForUrl with multi value', function() {
- beforeEach(function() {
- _templateSrv.init([{ name: 'test', current: { value: ['val1', 'val2'] }}]);
- });
-
- it('should set multiple url params', function() {
- var params = {};
- _templateSrv.fillVariableValuesForUrl(params);
- expect(params['var-test']).to.eql(['val1', 'val2']);
- });
- });
-
- describe('fillVariableValuesForUrl with multi value and scopedVars', function() {
- beforeEach(function() {
- _templateSrv.init([{ name: 'test', current: { value: ['val1', 'val2'] }}]);
- });
-
- it('should set multiple url params', function() {
- var params = {};
- _templateSrv.fillVariableValuesForUrl(params, {'test': {value: 'val1'}});
- expect(params['var-test']).to.eql('val1');
- });
- });
-
- describe('replaceWithText', function() {
- beforeEach(function() {
- _templateSrv.init([
- { name: 'server', current: { value: '{asd,asd2}', text: 'All' } },
- { name: 'period', current: { value: '$__auto_interval', text: 'auto' } }
- ]);
- _templateSrv.setGrafanaVariable('$__auto_interval', '13m');
- _templateSrv.updateTemplateData();
- });
-
- it('should replace with text except for grafanaVariables', function() {
- var target = _templateSrv.replaceWithText('Server: $server, period: $period');
- expect(target).to.be('Server: All, period: 13m');
- });
- });
-
- });
-
-});
diff --git a/public/test/specs/unsavedChangesSrv-specs.js b/public/test/specs/unsavedChangesSrv-specs.js
index ddbbb557055..a304279b77b 100644
--- a/public/test/specs/unsavedChangesSrv-specs.js
+++ b/public/test/specs/unsavedChangesSrv-specs.js
@@ -1,6 +1,6 @@
define([
'app/features/dashboard/unsavedChangesSrv',
- 'app/features/dashboard/dashboardSrv'
+ 'app/features/dashboard/dashboard_srv'
], function() {
'use strict';
@@ -14,6 +14,7 @@ define([
var dash;
var scope;
+ beforeEach(module('grafana.core'));
beforeEach(module('grafana.services'));
beforeEach(module(function($provide) {
$provide.value('contextSrv', _contextSrvStub);
diff --git a/public/vendor/flot/jquery.flot.js b/public/vendor/flot/jquery.flot.js
index 45d76b440c3..ec91314c49d 100644
--- a/public/vendor/flot/jquery.flot.js
+++ b/public/vendor/flot/jquery.flot.js
@@ -248,7 +248,7 @@ Licensed under the MIT license.
// Create the text layer container, if it doesn't exist
if (this.textContainer == null) {
- this.textContainer = $("
")
+ this.textContainer = $("
")
.css({
position: "absolute",
top: 0,
@@ -1319,7 +1319,7 @@ Licensed under the MIT license.
// Make sure the placeholder is clear of everything except canvases
// from a previous plot in this container that we'll try to re-use.
- placeholder.find(".flot-text").remove();
+ placeholder.find(".flot-temp-elem").remove();
if (placeholder.css("position") == 'static')
placeholder.css("position", "relative"); // for positioning labels and overlay
@@ -1663,8 +1663,10 @@ Licensed under the MIT license.
delta = max - min;
if (delta == 0.0) {
- // degenerate case
- var widen = max == 0 ? 1 : 0.01;
+ // Grafana fix: wide Y min and max using increased wideFactor
+ // when all series values are the same
+ var wideFactor = 0.25;
+ var widen = max == 0 ? 1 : max * wideFactor;
if (opts.min == null)
min -= widen;
diff --git a/public/views/index.html b/public/views/index.html
index a54d2c0c166..8d231ed2b68 100644
--- a/public/views/index.html
+++ b/public/views/index.html
@@ -64,13 +64,13 @@
Grafana
v[[.BuildVersion]] (commit: [[.BuildCommit]])
-
- [[if .NewGrafanaVersionExists]]
+ [[if .NewGrafanaVersionExists]]
+
New version available!
- [[end]]
-
+
+ [[end]]
diff --git a/scripts/circle-test.sh b/scripts/circle-test.sh
old mode 100644
new mode 100755
index b00bf7459ad..8918de82948
--- a/scripts/circle-test.sh
+++ b/scripts/circle-test.sh
@@ -25,6 +25,7 @@ exit_if_fail npm run coveralls
test -z "$(gofmt -s -l ./pkg/... | tee /dev/stderr)"
+exit_if_fail go run build.go setup
exit_if_fail go run build.go build
exit_if_fail go vet ./pkg/...
diff --git a/scripts/import_many_dashboards.sh b/scripts/import_many_dashboards.sh
new file mode 100755
index 00000000000..408732a2256
--- /dev/null
+++ b/scripts/import_many_dashboards.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+for index in {0..3000}
+do
+ echo -n "index $index"
+ curl 'http://localhost:3000/api/dashboards/import' -H 'Pragma: no-cache' -H 'Origin: http://localhost:3000' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: en-US,en;q=0.8,sv;q=0.6' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.101 Safari/537.36' -H 'Content-Type: application/json;charset=UTF-8' -H 'Accept: application/json, text/plain, */*' -H 'Cache-Control: no-cache' -H 'Referer: http://localhost:3000/dashboard/new?editview=import' -H 'Cookie: grafana_sess=662a67f11b47e657; grafana_user=admin; grafana_remember=bd839923f24f648c7cb53ede6ff9ef40826204e9a22df8f9; toggles=%7B%7D' -H 'Connection: keep-alive' --data-binary $'{"dashboard":{"__inputs":[{"name":"DS_GRAPHITE","label":"graphite","description":"","type":"datasource","pluginId":"graphite","pluginName":"Graphite"}],"__requires":[{"type":"panel","id":"singlestat","name":"Singlestat","version":""},{"type":"panel","id":"graph","name":"Graph","version":""},{"type":"grafana","id":"grafana","name":"Grafana","version":"3.1.0"},{"type":"datasource","id":"graphite","name":"Graphite","version":"1.0.0"}],"id":null,"title":"Big Dashboard dashname '"$index"$'","tags":["startpage","home","presentation"],"style":"dark","timezone":"browser","editable":true,"hideControls":false,"sharedCrosshair":true,"rows":[{"collapse":false,"editable":true,"height":"100px","panels":[{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"id":16,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":3,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":true},"targets":[{"refId":"A","target":"apps.backend.backend_02.counters.requests.count"}],"thresholds":"100,270","title":"Sign ups","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"id":15,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":3,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":true},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.7)"}],"thresholds":"100,270","title":"Logins","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"id":17,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":3,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":true},"targets":[{"refId":"A","target":"apps.backend.backend_04.counters.requests.count"}],"thresholds":"100,270","title":"Sign outs","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":100,"minValue":0,"show":false,"thresholdLabels":false,"thresholdMarkers":true},"id":18,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":3,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":true},"targets":[{"refId":"A","target":"scale(apps.backend.backend_03.counters.requests.count, 0.3)"}],"thresholds":"100,270","title":"Support calls","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1}],"title":"New row"},{"collapse":false,"editable":true,"height":218.4375,"panels":[{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":300,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":20,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":2,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.7)"}],"thresholds":"200,270","title":"Logins","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":300,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":24,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":2,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.2)"}],"thresholds":"200,270","title":"Google hits","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"bytes","gauge":{"maxValue":300,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":22,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":2,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.4)"}],"thresholds":"200,270","title":"Memory","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":300,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":21,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":2,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.8)"}],"thresholds":"200,270","title":"Logouts","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":300,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":26,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":2,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.2)"}],"thresholds":"200,270","title":"Google hits","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1},{"cacheTimeout":null,"colorBackground":false,"colorValue":true,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_GRAPHITE}","editable":true,"error":false,"format":"none","gauge":{"maxValue":300,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":25,"interval":null,"links":[],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","span":2,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":true,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"refId":"A","target":"scale(apps.backend.backend_01.counters.requests.count, 0.8)"}],"thresholds":"200,270","title":"Logouts","type":"singlestat","valueFontSize":"100%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"avg","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1}],"title":"New row"},{"collapsable":true,"collapse":false,"editable":true,"height":"250px","notice":false,"panels":[{"aliasColors":{"cpu":"#E24D42","memory":"#6ED0E0","statsd.fakesite.counters.session_start.desktop.count":"#6ED0E0"},"annotate":{"enable":false},"bars":false,"datasource":"${DS_GRAPHITE}","editable":true,"fill":3,"grid":{"max":null,"min":0,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":4,"interactive":true,"legend":{"avg":false,"current":true,"max":false,"min":true,"show":true,"total":false,"values":false},"legend_counts":true,"lines":true,"linewidth":2,"nullPointMode":"connected","options":false,"percentage":false,"pointradius":5,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[{"alias":"cpu","fill":0,"lines":true,"yaxis":2,"zindex":2},{"alias":"memory","pointradius":2,"points":true}],"span":4,"spyable":true,"stack":false,"steppedLine":false,"targets":[{"hide":false,"refId":"A","target":"alias(movingAverage(scaleToSeconds(apps.fakesite.web_server_01.counters.request_status.code_302.count, 10), 20), \'cpu\')"},{"refId":"B","target":"alias(statsd.fakesite.counters.session_start.desktop.count, \'memory\')"}],"timeFrom":null,"timeShift":null,"timezone":"browser","title":"Memory / CPU","tooltip":{"msResolution":false,"query_as_alias":true,"shared":false,"value_type":"cumulative","sort":0},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"bytes","logBase":1,"max":null,"min":null,"show":true},{"format":"percent","logBase":1,"max":null,"min":0,"show":true}],"zerofill":true},{"aliasColors":{"logins":"#7EB26D","logins (-1 day)":"#447EBC"},"annotate":{"enable":false},"bars":false,"datasource":"${DS_GRAPHITE}","editable":true,"fill":1,"grid":{"max":null,"min":0,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":3,"interactive":true,"legend":{"alignAsTable":false,"avg":false,"current":true,"max":true,"min":true,"rightSide":false,"show":true,"total":false,"values":false},"legend_counts":true,"lines":true,"linewidth":1,"nullPointMode":"connected","options":false,"percentage":false,"pointradius":5,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[],"span":4,"spyable":true,"stack":true,"steppedLine":false,"targets":[{"refId":"A","target":"alias(movingAverage(scaleToSeconds(apps.fakesite.web_server_01.counters.requests.count, 1), 2), \'logins\')"},{"refId":"B","target":"alias(movingAverage(timeShift(scaleToSeconds(apps.fakesite.web_server_01.counters.requests.count, 1), \'1h\'), 2), \'logins (-1 hour)\')"}],"timeFrom":null,"timeShift":"1h","timezone":"browser","title":"logins","tooltip":{"msResolution":false,"query_as_alias":true,"shared":false,"value_type":"cumulative","sort":0},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"short","logBase":1,"max":null,"min":null,"show":true},{"format":"short","logBase":1,"max":null,"min":null,"show":true}],"zerofill":true},{"aliasColors":{"cpu":"#E24D42","memory":"#6ED0E0","statsd.fakesite.counters.session_start.desktop.count":"#6ED0E0"},"annotate":{"enable":false},"bars":false,"datasource":"${DS_GRAPHITE}","editable":true,"fill":3,"grid":{"max":null,"min":0,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":19,"interactive":true,"legend":{"avg":false,"current":true,"max":false,"min":true,"show":true,"total":false,"values":false},"legend_counts":true,"lines":true,"linewidth":2,"nullPointMode":"connected","options":false,"percentage":false,"pointradius":5,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[{"alias":"cpu","fill":0,"lines":true,"yaxis":2,"zindex":2},{"alias":"memory","pointradius":2,"points":true}],"span":4,"spyable":true,"stack":false,"steppedLine":false,"targets":[{"hide":false,"refId":"A","target":"alias(movingAverage(scaleToSeconds(apps.fakesite.web_server_01.counters.request_status.code_302.count, 10), 20), \'cpu\')"},{"refId":"B","target":"alias(statsd.fakesite.counters.session_start.desktop.count, \'memory\')"}],"timeFrom":null,"timeShift":"1h","timezone":"browser","title":"Memory / CPU","tooltip":{"msResolution":false,"query_as_alias":true,"shared":false,"value_type":"cumulative","sort":0},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"bytes","logBase":1,"max":null,"min":null,"show":true},{"format":"percent","logBase":1,"max":null,"min":0,"show":true}],"zerofill":true}],"title":"test"},{"collapsable":true,"collapse":false,"editable":true,"height":"300px","notice":false,"panels":[{"aliasColors":{"web_server_01":"#B7DBAB","web_server_02":"#7EB26D","web_server_03":"#508642","web_server_04":"#3F6833"},"annotate":{"enable":false},"bars":false,"datasource":"${DS_GRAPHITE}","editable":true,"fill":8,"grid":{"max":null,"min":0,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":2,"interactive":true,"legend":{"alignAsTable":false,"avg":false,"current":false,"max":false,"min":false,"rightSide":false,"show":true,"total":false,"values":false},"legend_counts":true,"lines":true,"linewidth":2,"nullPointMode":"connected","options":false,"percentage":false,"pointradius":5,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[],"span":4,"spyable":true,"stack":true,"steppedLine":false,"targets":[{"refId":"A","target":"aliasByNode(movingAverage(scaleToSeconds(apps.fakesite.*.counters.requests.count, 1), 2), 2)"}],"timeFrom":null,"timeShift":null,"timezone":"browser","title":"server requests","tooltip":{"msResolution":false,"query_as_alias":true,"shared":true,"value_type":"cumulative","sort":0},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"short","logBase":1,"max":null,"min":null,"show":true},{"format":"short","logBase":1,"max":null,"min":null,"show":true}],"zerofill":true},{"aliasColors":{"upper_25":"#F9E2D2","upper_50":"#F2C96D","upper_75":"#EAB839"},"annotate":{"enable":false},"bars":true,"datasource":"${DS_GRAPHITE}","editable":true,"fill":1,"grid":{"max":null,"min":0,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":5,"interactive":true,"legend":{"alignAsTable":true,"avg":true,"current":false,"max":false,"min":false,"rightSide":true,"show":true,"total":false,"values":true},"legend_counts":true,"lines":false,"linewidth":2,"nullPointMode":"connected","options":false,"percentage":false,"pointradius":5,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[],"span":4,"spyable":true,"stack":true,"steppedLine":false,"targets":[{"refId":"A","target":"aliasByNode(summarize(statsd.fakesite.timers.ads_timer.*, \'4min\', \'avg\'), 4)"}],"timeFrom":null,"timeShift":null,"timezone":"browser","title":"client side full page load","tooltip":{"msResolution":false,"query_as_alias":true,"shared":false,"value_type":"cumulative","sort":0},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"ms","logBase":1,"max":null,"min":null,"show":true},{"format":"short","logBase":1,"max":null,"min":null,"show":true}],"zerofill":true},{"aliasColors":{"web_server_01":"#B7DBAB","web_server_02":"#7EB26D","web_server_03":"#508642","web_server_04":"#3F6833"},"annotate":{"enable":false},"bars":false,"datasource":"${DS_GRAPHITE}","editable":true,"fill":8,"grid":{"max":null,"min":0,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":14,"interactive":true,"legend":{"alignAsTable":false,"avg":false,"current":false,"max":false,"min":false,"rightSide":false,"show":true,"total":false,"values":false},"legend_counts":true,"lines":true,"linewidth":2,"nullPointMode":"connected","options":false,"percentage":false,"pointradius":5,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[],"span":4,"spyable":true,"stack":true,"steppedLine":false,"targets":[{"refId":"A","target":"aliasByNode(movingAverage(scaleToSeconds(apps.fakesite.*.counters.requests.count, 1), 2), 2)"}],"timeFrom":null,"timeShift":null,"timezone":"browser","title":"server requests","tooltip":{"msResolution":false,"query_as_alias":true,"shared":true,"value_type":"cumulative","sort":0},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"short","logBase":1,"max":null,"min":null,"show":true},{"format":"short","logBase":1,"max":null,"min":null,"show":true}],"zerofill":true}],"title":""},{"collapsable":true,"collapse":false,"editable":true,"height":"200px","notice":false,"panels":[{"aliasColors":{"cpu1":"#EF843C","cpu2":"#EAB839","upper_25":"#B7DBAB","upper_50":"#7EB26D","upper_75":"#629E51","upper_90":"#629E51","upper_95":"#508642"},"annotate":{"enable":false},"bars":false,"datasource":"${DS_GRAPHITE}","editable":true,"fill":3,"grid":{"max":null,"min":null,"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":6,"interactive":true,"legend":{"alignAsTable":true,"avg":true,"current":true,"legendSideLastValue":true,"max":false,"min":false,"rightSide":true,"show":false,"total":false,"values":true},"legend_counts":true,"lines":true,"linewidth":2,"links":[],"nullPointMode":"connected","options":false,"percentage":false,"pointradius":1,"points":false,"renderer":"flot","resolution":100,"scale":1,"seriesOverrides":[{"alias":"this is test of brekaing","yaxis":1}],"span":12,"spyable":true,"stack":false,"steppedLine":false,"targets":[{"refId":"A","target":"aliasByNode(statsd.fakesite.timers.ads_timer.*,4)"},{"refId":"B","target":"alias(scale(statsd.fakesite.timers.ads_timer.upper_95,-1),\'cpu1\')"},{"refId":"C","target":"alias(scale(statsd.fakesite.timers.ads_timer.upper_75,-1),\'cpu2\')"}],"timeFrom":null,"timeShift":null,"timezone":"browser","title":"","tooltip":{"msResolution":false,"query_as_alias":true,"shared":false,"value_type":"cumulative","sort":0},"transparent":true,"type":"graph","xaxis":{"show":false},"yaxes":[{"format":"ms","logBase":1,"max":null,"min":null,"show":false},{"format":"short","logBase":1,"max":null,"min":null,"show":false}],"zerofill":true}],"title":"test"}],"time":{"from":"now-30m","to":"now"},"timepicker":{"collapse":false,"enable":true,"notice":false,"now":true,"refresh_intervals":["5s","10s","30s","1m","5m","15m","30m","1h","2h","1d"],"status":"Stable","time_options":["5m","15m","1h","2h"," 6h","12h","24h","2d","7d","30d"],"type":"timepicker"},"templating":{"enable":false,"list":[]},"annotations":{"enable":false,"list":[]},"refresh":false,"schemaVersion":12,"version":5,"links":[],"gnetId":null},"overwrite":true,"inputs":[{"name":"DS_GRAPHITE","type":"datasource","pluginId":"graphite","value":"graphite"}]}' --compressed
+done
+
diff --git a/tasks/options/phantomjs.js b/tasks/options/phantomjs.js
index 4339f9880b6..ab3bd072652 100644
--- a/tasks/options/phantomjs.js
+++ b/tasks/options/phantomjs.js
@@ -1,34 +1,33 @@
-module.exports = function(config,grunt) {
- 'use strict';
-
- grunt.registerTask('phantomjs', 'Copy phantomjs binary from node', function() {
-
- var dest = './vendor/phantomjs/phantomjs';
- var confDir = './node_modules/phantomjs-prebuilt/lib/';
-
- if (!grunt.file.exists(dest)){
-
- var m=grunt.file.read(confDir+"location.js")
- var src=/= \"([^\"]*)\"/.exec(m)[1];
-
- if (!grunt.file.isPathAbsolute(src)) {
- src = confDir+src;
- }
-
- try {
- grunt.config('copy.phantom_bin', {
- src: src,
- dest: dest,
- options: { mode: true},
- });
- grunt.task.run('copy:phantom_bin');
- } catch (err) {
- grunt.verbose.writeln(err);
- grunt.fail.warn('No working Phantomjs binary available')
- }
-
- } else {
- grunt.log.writeln('Phantomjs already imported from node');
- }
- });
-};
+module.exports = function(config,grunt) {
+ 'use strict';
+
+ grunt.registerTask('phantomjs', 'Copy phantomjs binary to vendor/', function() {
+
+ var dest = './vendor/phantomjs/phantomjs';
+ var confDir = './node_modules/phantomjs-prebuilt/lib/';
+
+ src = config.phjs
+
+ if (!src){
+ var m=grunt.file.read(confDir+"location.js")
+ var src=/= \"([^\"]*)\"/.exec(m)[1];
+
+ if (!grunt.file.isPathAbsolute(src)) {
+ src = confDir+src;
+ }
+ }
+
+ try {
+ grunt.config('copy.phantom_bin', {
+ src: src,
+ dest: dest,
+ options: { mode: true},
+ });
+ grunt.task.run('copy:phantom_bin');
+ } catch (err) {
+ grunt.verbose.writeln(err);
+ grunt.fail.warn('No working Phantomjs binary available')
+ }
+
+ });
+};
diff --git a/vendor/github.com/prometheus/client_golang/LICENSE b/vendor/github.com/prometheus/client_golang/LICENSE
new file mode 100644
index 00000000000..261eeb9e9f8
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/prometheus/client_golang/NOTICE b/vendor/github.com/prometheus/client_golang/NOTICE
new file mode 100644
index 00000000000..dd878a30ee9
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/NOTICE
@@ -0,0 +1,23 @@
+Prometheus instrumentation library for Go applications
+Copyright 2012-2015 The Prometheus Authors
+
+This product includes software developed at
+SoundCloud Ltd. (http://soundcloud.com/).
+
+
+The following components are included in this product:
+
+perks - a fork of https://github.com/bmizerany/perks
+https://github.com/beorn7/perks
+Copyright 2013-2015 Blake Mizerany, Björn Rabenstein
+See https://github.com/beorn7/perks/blob/master/README.md for license details.
+
+Go support for Protocol Buffers - Google's data interchange format
+http://github.com/golang/protobuf/
+Copyright 2010 The Go Authors
+See source code for license details.
+
+Support for streaming Protocol Buffer messages for the Go language (golang).
+https://github.com/matttproud/golang_protobuf_extensions
+Copyright 2013 Matt T. Proud
+Licensed under the Apache License, Version 2.0
diff --git a/vendor/github.com/prometheus/client_golang/api/prometheus/api.go b/vendor/github.com/prometheus/client_golang/api/prometheus/api.go
new file mode 100644
index 00000000000..cc5cbc364d3
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/api/prometheus/api.go
@@ -0,0 +1,348 @@
+// Copyright 2015 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package prometheus provides bindings to the Prometheus HTTP API:
+// http://prometheus.io/docs/querying/api/
+package prometheus
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "net/url"
+ "path"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/prometheus/common/model"
+ "golang.org/x/net/context"
+ "golang.org/x/net/context/ctxhttp"
+)
+
+const (
+ statusAPIError = 422
+ apiPrefix = "/api/v1"
+
+ epQuery = "/query"
+ epQueryRange = "/query_range"
+ epLabelValues = "/label/:name/values"
+ epSeries = "/series"
+)
+
+// ErrorType models the different API error types.
+type ErrorType string
+
+// Possible values for ErrorType.
+const (
+ ErrBadData ErrorType = "bad_data"
+ ErrTimeout = "timeout"
+ ErrCanceled = "canceled"
+ ErrExec = "execution"
+ ErrBadResponse = "bad_response"
+)
+
+// Error is an error returned by the API.
+type Error struct {
+ Type ErrorType
+ Msg string
+}
+
+func (e *Error) Error() string {
+ return fmt.Sprintf("%s: %s", e.Type, e.Msg)
+}
+
+// CancelableTransport is like net.Transport but provides
+// per-request cancelation functionality.
+type CancelableTransport interface {
+ http.RoundTripper
+ CancelRequest(req *http.Request)
+}
+
+// DefaultTransport is used if no Transport is set in Config.
+var DefaultTransport CancelableTransport = &http.Transport{
+ Proxy: http.ProxyFromEnvironment,
+ Dial: (&net.Dialer{
+ Timeout: 30 * time.Second,
+ KeepAlive: 30 * time.Second,
+ }).Dial,
+ TLSHandshakeTimeout: 10 * time.Second,
+}
+
+// Config defines configuration parameters for a new client.
+type Config struct {
+ // The address of the Prometheus to connect to.
+ Address string
+
+ // Transport is used by the Client to drive HTTP requests. If not
+ // provided, DefaultTransport will be used.
+ Transport CancelableTransport
+}
+
+func (cfg *Config) transport() CancelableTransport {
+ if cfg.Transport == nil {
+ return DefaultTransport
+ }
+ return cfg.Transport
+}
+
+// Client is the interface for an API client.
+type Client interface {
+ url(ep string, args map[string]string) *url.URL
+ do(context.Context, *http.Request) (*http.Response, []byte, error)
+}
+
+// New returns a new Client.
+//
+// It is safe to use the returned Client from multiple goroutines.
+func New(cfg Config) (Client, error) {
+ u, err := url.Parse(cfg.Address)
+ if err != nil {
+ return nil, err
+ }
+ u.Path = strings.TrimRight(u.Path, "/") + apiPrefix
+
+ return &httpClient{
+ endpoint: u,
+ transport: cfg.transport(),
+ }, nil
+}
+
+type httpClient struct {
+ endpoint *url.URL
+ transport CancelableTransport
+}
+
+func (c *httpClient) url(ep string, args map[string]string) *url.URL {
+ p := path.Join(c.endpoint.Path, ep)
+
+ for arg, val := range args {
+ arg = ":" + arg
+ p = strings.Replace(p, arg, val, -1)
+ }
+
+ u := *c.endpoint
+ u.Path = p
+
+ return &u
+}
+
+func (c *httpClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
+ resp, err := ctxhttp.Do(ctx, &http.Client{Transport: c.transport}, req)
+
+ defer func() {
+ if resp != nil {
+ resp.Body.Close()
+ }
+ }()
+
+ if err != nil {
+ return nil, nil, err
+ }
+
+ var body []byte
+ done := make(chan struct{})
+ go func() {
+ body, err = ioutil.ReadAll(resp.Body)
+ close(done)
+ }()
+
+ select {
+ case <-ctx.Done():
+ err = resp.Body.Close()
+ <-done
+ if err == nil {
+ err = ctx.Err()
+ }
+ case <-done:
+ }
+
+ return resp, body, err
+}
+
+// apiClient wraps a regular client and processes successful API responses.
+// Successful also includes responses that errored at the API level.
+type apiClient struct {
+ Client
+}
+
+type apiResponse struct {
+ Status string `json:"status"`
+ Data json.RawMessage `json:"data"`
+ ErrorType ErrorType `json:"errorType"`
+ Error string `json:"error"`
+}
+
+func (c apiClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
+ resp, body, err := c.Client.do(ctx, req)
+ if err != nil {
+ return resp, body, err
+ }
+
+ code := resp.StatusCode
+
+ if code/100 != 2 && code != statusAPIError {
+ return resp, body, &Error{
+ Type: ErrBadResponse,
+ Msg: fmt.Sprintf("bad response code %d", resp.StatusCode),
+ }
+ }
+
+ var result apiResponse
+
+ if err = json.Unmarshal(body, &result); err != nil {
+ return resp, body, &Error{
+ Type: ErrBadResponse,
+ Msg: err.Error(),
+ }
+ }
+
+ if (code == statusAPIError) != (result.Status == "error") {
+ err = &Error{
+ Type: ErrBadResponse,
+ Msg: "inconsistent body for response code",
+ }
+ }
+
+ if code == statusAPIError && result.Status == "error" {
+ err = &Error{
+ Type: result.ErrorType,
+ Msg: result.Error,
+ }
+ }
+
+ return resp, []byte(result.Data), err
+}
+
+// Range represents a sliced time range.
+type Range struct {
+ // The boundaries of the time range.
+ Start, End time.Time
+ // The maximum time between two slices within the boundaries.
+ Step time.Duration
+}
+
+// queryResult contains result data for a query.
+type queryResult struct {
+ Type model.ValueType `json:"resultType"`
+ Result interface{} `json:"result"`
+
+ // The decoded value.
+ v model.Value
+}
+
+func (qr *queryResult) UnmarshalJSON(b []byte) error {
+ v := struct {
+ Type model.ValueType `json:"resultType"`
+ Result json.RawMessage `json:"result"`
+ }{}
+
+ err := json.Unmarshal(b, &v)
+ if err != nil {
+ return err
+ }
+
+ switch v.Type {
+ case model.ValScalar:
+ var sv model.Scalar
+ err = json.Unmarshal(v.Result, &sv)
+ qr.v = &sv
+
+ case model.ValVector:
+ var vv model.Vector
+ err = json.Unmarshal(v.Result, &vv)
+ qr.v = vv
+
+ case model.ValMatrix:
+ var mv model.Matrix
+ err = json.Unmarshal(v.Result, &mv)
+ qr.v = mv
+
+ default:
+ err = fmt.Errorf("unexpected value type %q", v.Type)
+ }
+ return err
+}
+
+// QueryAPI provides bindings the Prometheus's query API.
+type QueryAPI interface {
+ // Query performs a query for the given time.
+ Query(ctx context.Context, query string, ts time.Time) (model.Value, error)
+ // Query performs a query for the given range.
+ QueryRange(ctx context.Context, query string, r Range) (model.Value, error)
+}
+
+// NewQueryAPI returns a new QueryAPI for the client.
+//
+// It is safe to use the returned QueryAPI from multiple goroutines.
+func NewQueryAPI(c Client) QueryAPI {
+ return &httpQueryAPI{client: apiClient{c}}
+}
+
+type httpQueryAPI struct {
+ client Client
+}
+
+func (h *httpQueryAPI) Query(ctx context.Context, query string, ts time.Time) (model.Value, error) {
+ u := h.client.url(epQuery, nil)
+ q := u.Query()
+
+ q.Set("query", query)
+ q.Set("time", ts.Format(time.RFC3339Nano))
+
+ u.RawQuery = q.Encode()
+
+ req, _ := http.NewRequest("GET", u.String(), nil)
+
+ _, body, err := h.client.do(ctx, req)
+ if err != nil {
+ return nil, err
+ }
+
+ var qres queryResult
+ err = json.Unmarshal(body, &qres)
+
+ return model.Value(qres.v), err
+}
+
+func (h *httpQueryAPI) QueryRange(ctx context.Context, query string, r Range) (model.Value, error) {
+ u := h.client.url(epQueryRange, nil)
+ q := u.Query()
+
+ var (
+ start = r.Start.Format(time.RFC3339Nano)
+ end = r.End.Format(time.RFC3339Nano)
+ step = strconv.FormatFloat(r.Step.Seconds(), 'f', 3, 64)
+ )
+
+ q.Set("query", query)
+ q.Set("start", start)
+ q.Set("end", end)
+ q.Set("step", step)
+
+ u.RawQuery = q.Encode()
+
+ req, _ := http.NewRequest("GET", u.String(), nil)
+
+ _, body, err := h.client.do(ctx, req)
+ if err != nil {
+ return nil, err
+ }
+
+ var qres queryResult
+ err = json.Unmarshal(body, &qres)
+
+ return model.Value(qres.v), err
+}
diff --git a/vendor/github.com/prometheus/common/LICENSE b/vendor/github.com/prometheus/common/LICENSE
new file mode 100644
index 00000000000..261eeb9e9f8
--- /dev/null
+++ b/vendor/github.com/prometheus/common/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/prometheus/common/NOTICE b/vendor/github.com/prometheus/common/NOTICE
new file mode 100644
index 00000000000..636a2c1a5e8
--- /dev/null
+++ b/vendor/github.com/prometheus/common/NOTICE
@@ -0,0 +1,5 @@
+Common libraries shared by Prometheus Go components.
+Copyright 2015 The Prometheus Authors
+
+This product includes software developed at
+SoundCloud Ltd. (http://soundcloud.com/).
diff --git a/vendor/github.com/prometheus/common/model/alert.go b/vendor/github.com/prometheus/common/model/alert.go
new file mode 100644
index 00000000000..35e739c7ad2
--- /dev/null
+++ b/vendor/github.com/prometheus/common/model/alert.go
@@ -0,0 +1,136 @@
+// Copyright 2013 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "fmt"
+ "time"
+)
+
+type AlertStatus string
+
+const (
+ AlertFiring AlertStatus = "firing"
+ AlertResolved AlertStatus = "resolved"
+)
+
+// Alert is a generic representation of an alert in the Prometheus eco-system.
+type Alert struct {
+ // Label value pairs for purpose of aggregation, matching, and disposition
+ // dispatching. This must minimally include an "alertname" label.
+ Labels LabelSet `json:"labels"`
+
+ // Extra key/value information which does not define alert identity.
+ Annotations LabelSet `json:"annotations"`
+
+ // The known time range for this alert. Both ends are optional.
+ StartsAt time.Time `json:"startsAt,omitempty"`
+ EndsAt time.Time `json:"endsAt,omitempty"`
+ GeneratorURL string `json:"generatorURL"`
+}
+
+// Name returns the name of the alert. It is equivalent to the "alertname" label.
+func (a *Alert) Name() string {
+ return string(a.Labels[AlertNameLabel])
+}
+
+// Fingerprint returns a unique hash for the alert. It is equivalent to
+// the fingerprint of the alert's label set.
+func (a *Alert) Fingerprint() Fingerprint {
+ return a.Labels.Fingerprint()
+}
+
+func (a *Alert) String() string {
+ s := fmt.Sprintf("%s[%s]", a.Name(), a.Fingerprint().String()[:7])
+ if a.Resolved() {
+ return s + "[resolved]"
+ }
+ return s + "[active]"
+}
+
+// Resolved returns true iff the activity interval ended in the past.
+func (a *Alert) Resolved() bool {
+ return a.ResolvedAt(time.Now())
+}
+
+// ResolvedAt returns true off the activity interval ended before
+// the given timestamp.
+func (a *Alert) ResolvedAt(ts time.Time) bool {
+ if a.EndsAt.IsZero() {
+ return false
+ }
+ return !a.EndsAt.After(ts)
+}
+
+// Status returns the status of the alert.
+func (a *Alert) Status() AlertStatus {
+ if a.Resolved() {
+ return AlertResolved
+ }
+ return AlertFiring
+}
+
+// Validate checks whether the alert data is inconsistent.
+func (a *Alert) Validate() error {
+ if a.StartsAt.IsZero() {
+ return fmt.Errorf("start time missing")
+ }
+ if !a.EndsAt.IsZero() && a.EndsAt.Before(a.StartsAt) {
+ return fmt.Errorf("start time must be before end time")
+ }
+ if err := a.Labels.Validate(); err != nil {
+ return fmt.Errorf("invalid label set: %s", err)
+ }
+ if len(a.Labels) == 0 {
+ return fmt.Errorf("at least one label pair required")
+ }
+ if err := a.Annotations.Validate(); err != nil {
+ return fmt.Errorf("invalid annotations: %s", err)
+ }
+ return nil
+}
+
+// Alert is a list of alerts that can be sorted in chronological order.
+type Alerts []*Alert
+
+func (as Alerts) Len() int { return len(as) }
+func (as Alerts) Swap(i, j int) { as[i], as[j] = as[j], as[i] }
+
+func (as Alerts) Less(i, j int) bool {
+ if as[i].StartsAt.Before(as[j].StartsAt) {
+ return true
+ }
+ if as[i].EndsAt.Before(as[j].EndsAt) {
+ return true
+ }
+ return as[i].Fingerprint() < as[j].Fingerprint()
+}
+
+// HasFiring returns true iff one of the alerts is not resolved.
+func (as Alerts) HasFiring() bool {
+ for _, a := range as {
+ if !a.Resolved() {
+ return true
+ }
+ }
+ return false
+}
+
+// Status returns StatusFiring iff at least one of the alerts is firing.
+func (as Alerts) Status() AlertStatus {
+ if as.HasFiring() {
+ return AlertFiring
+ }
+ return AlertResolved
+}
diff --git a/vendor/github.com/prometheus/common/model/fingerprinting.go b/vendor/github.com/prometheus/common/model/fingerprinting.go
new file mode 100644
index 00000000000..fc4de4106e8
--- /dev/null
+++ b/vendor/github.com/prometheus/common/model/fingerprinting.go
@@ -0,0 +1,105 @@
+// Copyright 2013 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "fmt"
+ "strconv"
+)
+
+// Fingerprint provides a hash-capable representation of a Metric.
+// For our purposes, FNV-1A 64-bit is used.
+type Fingerprint uint64
+
+// FingerprintFromString transforms a string representation into a Fingerprint.
+func FingerprintFromString(s string) (Fingerprint, error) {
+ num, err := strconv.ParseUint(s, 16, 64)
+ return Fingerprint(num), err
+}
+
+// ParseFingerprint parses the input string into a fingerprint.
+func ParseFingerprint(s string) (Fingerprint, error) {
+ num, err := strconv.ParseUint(s, 16, 64)
+ if err != nil {
+ return 0, err
+ }
+ return Fingerprint(num), nil
+}
+
+func (f Fingerprint) String() string {
+ return fmt.Sprintf("%016x", uint64(f))
+}
+
+// Fingerprints represents a collection of Fingerprint subject to a given
+// natural sorting scheme. It implements sort.Interface.
+type Fingerprints []Fingerprint
+
+// Len implements sort.Interface.
+func (f Fingerprints) Len() int {
+ return len(f)
+}
+
+// Less implements sort.Interface.
+func (f Fingerprints) Less(i, j int) bool {
+ return f[i] < f[j]
+}
+
+// Swap implements sort.Interface.
+func (f Fingerprints) Swap(i, j int) {
+ f[i], f[j] = f[j], f[i]
+}
+
+// FingerprintSet is a set of Fingerprints.
+type FingerprintSet map[Fingerprint]struct{}
+
+// Equal returns true if both sets contain the same elements (and not more).
+func (s FingerprintSet) Equal(o FingerprintSet) bool {
+ if len(s) != len(o) {
+ return false
+ }
+
+ for k := range s {
+ if _, ok := o[k]; !ok {
+ return false
+ }
+ }
+
+ return true
+}
+
+// Intersection returns the elements contained in both sets.
+func (s FingerprintSet) Intersection(o FingerprintSet) FingerprintSet {
+ myLength, otherLength := len(s), len(o)
+ if myLength == 0 || otherLength == 0 {
+ return FingerprintSet{}
+ }
+
+ subSet := s
+ superSet := o
+
+ if otherLength < myLength {
+ subSet = o
+ superSet = s
+ }
+
+ out := FingerprintSet{}
+
+ for k := range subSet {
+ if _, ok := superSet[k]; ok {
+ out[k] = struct{}{}
+ }
+ }
+
+ return out
+}
diff --git a/vendor/github.com/prometheus/common/model/fnv.go b/vendor/github.com/prometheus/common/model/fnv.go
new file mode 100644
index 00000000000..038fc1c9003
--- /dev/null
+++ b/vendor/github.com/prometheus/common/model/fnv.go
@@ -0,0 +1,42 @@
+// Copyright 2015 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+// Inline and byte-free variant of hash/fnv's fnv64a.
+
+const (
+ offset64 = 14695981039346656037
+ prime64 = 1099511628211
+)
+
+// hashNew initializies a new fnv64a hash value.
+func hashNew() uint64 {
+ return offset64
+}
+
+// hashAdd adds a string to a fnv64a hash value, returning the updated hash.
+func hashAdd(h uint64, s string) uint64 {
+ for i := 0; i < len(s); i++ {
+ h ^= uint64(s[i])
+ h *= prime64
+ }
+ return h
+}
+
+// hashAddByte adds a byte to a fnv64a hash value, returning the updated hash.
+func hashAddByte(h uint64, b byte) uint64 {
+ h ^= uint64(b)
+ h *= prime64
+ return h
+}
diff --git a/vendor/github.com/prometheus/common/model/labels.go b/vendor/github.com/prometheus/common/model/labels.go
new file mode 100644
index 00000000000..3b72e7ff8f6
--- /dev/null
+++ b/vendor/github.com/prometheus/common/model/labels.go
@@ -0,0 +1,206 @@
+// Copyright 2013 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "encoding/json"
+ "fmt"
+ "regexp"
+ "strings"
+ "unicode/utf8"
+)
+
+const (
+ // AlertNameLabel is the name of the label containing the an alert's name.
+ AlertNameLabel = "alertname"
+
+ // ExportedLabelPrefix is the prefix to prepend to the label names present in
+ // exported metrics if a label of the same name is added by the server.
+ ExportedLabelPrefix = "exported_"
+
+ // MetricNameLabel is the label name indicating the metric name of a
+ // timeseries.
+ MetricNameLabel = "__name__"
+
+ // SchemeLabel is the name of the label that holds the scheme on which to
+ // scrape a target.
+ SchemeLabel = "__scheme__"
+
+ // AddressLabel is the name of the label that holds the address of
+ // a scrape target.
+ AddressLabel = "__address__"
+
+ // MetricsPathLabel is the name of the label that holds the path on which to
+ // scrape a target.
+ MetricsPathLabel = "__metrics_path__"
+
+ // ReservedLabelPrefix is a prefix which is not legal in user-supplied
+ // label names.
+ ReservedLabelPrefix = "__"
+
+ // MetaLabelPrefix is a prefix for labels that provide meta information.
+ // Labels with this prefix are used for intermediate label processing and
+ // will not be attached to time series.
+ MetaLabelPrefix = "__meta_"
+
+ // TmpLabelPrefix is a prefix for temporary labels as part of relabelling.
+ // Labels with this prefix are used for intermediate label processing and
+ // will not be attached to time series. This is reserved for use in
+ // Prometheus configuration files by users.
+ TmpLabelPrefix = "__tmp_"
+
+ // ParamLabelPrefix is a prefix for labels that provide URL parameters
+ // used to scrape a target.
+ ParamLabelPrefix = "__param_"
+
+ // JobLabel is the label name indicating the job from which a timeseries
+ // was scraped.
+ JobLabel = "job"
+
+ // InstanceLabel is the label name used for the instance label.
+ InstanceLabel = "instance"
+
+ // BucketLabel is used for the label that defines the upper bound of a
+ // bucket of a histogram ("le" -> "less or equal").
+ BucketLabel = "le"
+
+ // QuantileLabel is used for the label that defines the quantile in a
+ // summary.
+ QuantileLabel = "quantile"
+)
+
+// LabelNameRE is a regular expression matching valid label names.
+var LabelNameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$")
+
+// A LabelName is a key for a LabelSet or Metric. It has a value associated
+// therewith.
+type LabelName string
+
+// IsValid is true iff the label name matches the pattern of LabelNameRE.
+func (ln LabelName) IsValid() bool {
+ if len(ln) == 0 {
+ return false
+ }
+ for i, b := range ln {
+ if !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || (b >= '0' && b <= '9' && i > 0)) {
+ return false
+ }
+ }
+ return true
+}
+
+// UnmarshalYAML implements the yaml.Unmarshaler interface.
+func (ln *LabelName) UnmarshalYAML(unmarshal func(interface{}) error) error {
+ var s string
+ if err := unmarshal(&s); err != nil {
+ return err
+ }
+ if !LabelNameRE.MatchString(s) {
+ return fmt.Errorf("%q is not a valid label name", s)
+ }
+ *ln = LabelName(s)
+ return nil
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (ln *LabelName) UnmarshalJSON(b []byte) error {
+ var s string
+ if err := json.Unmarshal(b, &s); err != nil {
+ return err
+ }
+ if !LabelNameRE.MatchString(s) {
+ return fmt.Errorf("%q is not a valid label name", s)
+ }
+ *ln = LabelName(s)
+ return nil
+}
+
+// LabelNames is a sortable LabelName slice. In implements sort.Interface.
+type LabelNames []LabelName
+
+func (l LabelNames) Len() int {
+ return len(l)
+}
+
+func (l LabelNames) Less(i, j int) bool {
+ return l[i] < l[j]
+}
+
+func (l LabelNames) Swap(i, j int) {
+ l[i], l[j] = l[j], l[i]
+}
+
+func (l LabelNames) String() string {
+ labelStrings := make([]string, 0, len(l))
+ for _, label := range l {
+ labelStrings = append(labelStrings, string(label))
+ }
+ return strings.Join(labelStrings, ", ")
+}
+
+// A LabelValue is an associated value for a LabelName.
+type LabelValue string
+
+// IsValid returns true iff the string is a valid UTF8.
+func (lv LabelValue) IsValid() bool {
+ return utf8.ValidString(string(lv))
+}
+
+// LabelValues is a sortable LabelValue slice. It implements sort.Interface.
+type LabelValues []LabelValue
+
+func (l LabelValues) Len() int {
+ return len(l)
+}
+
+func (l LabelValues) Less(i, j int) bool {
+ return string(l[i]) < string(l[j])
+}
+
+func (l LabelValues) Swap(i, j int) {
+ l[i], l[j] = l[j], l[i]
+}
+
+// LabelPair pairs a name with a value.
+type LabelPair struct {
+ Name LabelName
+ Value LabelValue
+}
+
+// LabelPairs is a sortable slice of LabelPair pointers. It implements
+// sort.Interface.
+type LabelPairs []*LabelPair
+
+func (l LabelPairs) Len() int {
+ return len(l)
+}
+
+func (l LabelPairs) Less(i, j int) bool {
+ switch {
+ case l[i].Name > l[j].Name:
+ return false
+ case l[i].Name < l[j].Name:
+ return true
+ case l[i].Value > l[j].Value:
+ return false
+ case l[i].Value < l[j].Value:
+ return true
+ default:
+ return false
+ }
+}
+
+func (l LabelPairs) Swap(i, j int) {
+ l[i], l[j] = l[j], l[i]
+}
diff --git a/vendor/github.com/prometheus/common/model/labelset.go b/vendor/github.com/prometheus/common/model/labelset.go
new file mode 100644
index 00000000000..5f931cdb9b3
--- /dev/null
+++ b/vendor/github.com/prometheus/common/model/labelset.go
@@ -0,0 +1,169 @@
+// Copyright 2013 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "encoding/json"
+ "fmt"
+ "sort"
+ "strings"
+)
+
+// A LabelSet is a collection of LabelName and LabelValue pairs. The LabelSet
+// may be fully-qualified down to the point where it may resolve to a single
+// Metric in the data store or not. All operations that occur within the realm
+// of a LabelSet can emit a vector of Metric entities to which the LabelSet may
+// match.
+type LabelSet map[LabelName]LabelValue
+
+// Validate checks whether all names and values in the label set
+// are valid.
+func (ls LabelSet) Validate() error {
+ for ln, lv := range ls {
+ if !ln.IsValid() {
+ return fmt.Errorf("invalid name %q", ln)
+ }
+ if !lv.IsValid() {
+ return fmt.Errorf("invalid value %q", lv)
+ }
+ }
+ return nil
+}
+
+// Equal returns true iff both label sets have exactly the same key/value pairs.
+func (ls LabelSet) Equal(o LabelSet) bool {
+ if len(ls) != len(o) {
+ return false
+ }
+ for ln, lv := range ls {
+ olv, ok := o[ln]
+ if !ok {
+ return false
+ }
+ if olv != lv {
+ return false
+ }
+ }
+ return true
+}
+
+// Before compares the metrics, using the following criteria:
+//
+// If m has fewer labels than o, it is before o. If it has more, it is not.
+//
+// If the number of labels is the same, the superset of all label names is
+// sorted alphanumerically. The first differing label pair found in that order
+// determines the outcome: If the label does not exist at all in m, then m is
+// before o, and vice versa. Otherwise the label value is compared
+// alphanumerically.
+//
+// If m and o are equal, the method returns false.
+func (ls LabelSet) Before(o LabelSet) bool {
+ if len(ls) < len(o) {
+ return true
+ }
+ if len(ls) > len(o) {
+ return false
+ }
+
+ lns := make(LabelNames, 0, len(ls)+len(o))
+ for ln := range ls {
+ lns = append(lns, ln)
+ }
+ for ln := range o {
+ lns = append(lns, ln)
+ }
+ // It's probably not worth it to de-dup lns.
+ sort.Sort(lns)
+ for _, ln := range lns {
+ mlv, ok := ls[ln]
+ if !ok {
+ return true
+ }
+ olv, ok := o[ln]
+ if !ok {
+ return false
+ }
+ if mlv < olv {
+ return true
+ }
+ if mlv > olv {
+ return false
+ }
+ }
+ return false
+}
+
+// Clone returns a copy of the label set.
+func (ls LabelSet) Clone() LabelSet {
+ lsn := make(LabelSet, len(ls))
+ for ln, lv := range ls {
+ lsn[ln] = lv
+ }
+ return lsn
+}
+
+// Merge is a helper function to non-destructively merge two label sets.
+func (l LabelSet) Merge(other LabelSet) LabelSet {
+ result := make(LabelSet, len(l))
+
+ for k, v := range l {
+ result[k] = v
+ }
+
+ for k, v := range other {
+ result[k] = v
+ }
+
+ return result
+}
+
+func (l LabelSet) String() string {
+ lstrs := make([]string, 0, len(l))
+ for l, v := range l {
+ lstrs = append(lstrs, fmt.Sprintf("%s=%q", l, v))
+ }
+
+ sort.Strings(lstrs)
+ return fmt.Sprintf("{%s}", strings.Join(lstrs, ", "))
+}
+
+// Fingerprint returns the LabelSet's fingerprint.
+func (ls LabelSet) Fingerprint() Fingerprint {
+ return labelSetToFingerprint(ls)
+}
+
+// FastFingerprint returns the LabelSet's Fingerprint calculated by a faster hashing
+// algorithm, which is, however, more susceptible to hash collisions.
+func (ls LabelSet) FastFingerprint() Fingerprint {
+ return labelSetToFastFingerprint(ls)
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (l *LabelSet) UnmarshalJSON(b []byte) error {
+ var m map[LabelName]LabelValue
+ if err := json.Unmarshal(b, &m); err != nil {
+ return err
+ }
+ // encoding/json only unmarshals maps of the form map[string]T. It treats
+ // LabelName as a string and does not call its UnmarshalJSON method.
+ // Thus, we have to replicate the behavior here.
+ for ln := range m {
+ if !LabelNameRE.MatchString(string(ln)) {
+ return fmt.Errorf("%q is not a valid label name", ln)
+ }
+ }
+ *l = LabelSet(m)
+ return nil
+}
diff --git a/vendor/github.com/prometheus/common/model/metric.go b/vendor/github.com/prometheus/common/model/metric.go
new file mode 100644
index 00000000000..a5da59a5055
--- /dev/null
+++ b/vendor/github.com/prometheus/common/model/metric.go
@@ -0,0 +1,98 @@
+// Copyright 2013 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "fmt"
+ "regexp"
+ "sort"
+ "strings"
+)
+
+var (
+ separator = []byte{0}
+ MetricNameRE = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_:]*$`)
+)
+
+// A Metric is similar to a LabelSet, but the key difference is that a Metric is
+// a singleton and refers to one and only one stream of samples.
+type Metric LabelSet
+
+// Equal compares the metrics.
+func (m Metric) Equal(o Metric) bool {
+ return LabelSet(m).Equal(LabelSet(o))
+}
+
+// Before compares the metrics' underlying label sets.
+func (m Metric) Before(o Metric) bool {
+ return LabelSet(m).Before(LabelSet(o))
+}
+
+// Clone returns a copy of the Metric.
+func (m Metric) Clone() Metric {
+ clone := Metric{}
+ for k, v := range m {
+ clone[k] = v
+ }
+ return clone
+}
+
+func (m Metric) String() string {
+ metricName, hasName := m[MetricNameLabel]
+ numLabels := len(m) - 1
+ if !hasName {
+ numLabels = len(m)
+ }
+ labelStrings := make([]string, 0, numLabels)
+ for label, value := range m {
+ if label != MetricNameLabel {
+ labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value))
+ }
+ }
+
+ switch numLabels {
+ case 0:
+ if hasName {
+ return string(metricName)
+ }
+ return "{}"
+ default:
+ sort.Strings(labelStrings)
+ return fmt.Sprintf("%s{%s}", metricName, strings.Join(labelStrings, ", "))
+ }
+}
+
+// Fingerprint returns a Metric's Fingerprint.
+func (m Metric) Fingerprint() Fingerprint {
+ return LabelSet(m).Fingerprint()
+}
+
+// FastFingerprint returns a Metric's Fingerprint calculated by a faster hashing
+// algorithm, which is, however, more susceptible to hash collisions.
+func (m Metric) FastFingerprint() Fingerprint {
+ return LabelSet(m).FastFingerprint()
+}
+
+// IsValidMetricName returns true iff name matches the pattern of MetricNameRE.
+func IsValidMetricName(n LabelValue) bool {
+ if len(n) == 0 {
+ return false
+ }
+ for i, b := range n {
+ if !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || b == ':' || (b >= '0' && b <= '9' && i > 0)) {
+ return false
+ }
+ }
+ return true
+}
diff --git a/vendor/github.com/prometheus/common/model/model.go b/vendor/github.com/prometheus/common/model/model.go
new file mode 100644
index 00000000000..a7b9691707e
--- /dev/null
+++ b/vendor/github.com/prometheus/common/model/model.go
@@ -0,0 +1,16 @@
+// Copyright 2013 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package model contains common data structures that are shared across
+// Prometheus components and libraries.
+package model
diff --git a/vendor/github.com/prometheus/common/model/signature.go b/vendor/github.com/prometheus/common/model/signature.go
new file mode 100644
index 00000000000..8762b13c63d
--- /dev/null
+++ b/vendor/github.com/prometheus/common/model/signature.go
@@ -0,0 +1,144 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "sort"
+)
+
+// SeparatorByte is a byte that cannot occur in valid UTF-8 sequences and is
+// used to separate label names, label values, and other strings from each other
+// when calculating their combined hash value (aka signature aka fingerprint).
+const SeparatorByte byte = 255
+
+var (
+ // cache the signature of an empty label set.
+ emptyLabelSignature = hashNew()
+)
+
+// LabelsToSignature returns a quasi-unique signature (i.e., fingerprint) for a
+// given label set. (Collisions are possible but unlikely if the number of label
+// sets the function is applied to is small.)
+func LabelsToSignature(labels map[string]string) uint64 {
+ if len(labels) == 0 {
+ return emptyLabelSignature
+ }
+
+ labelNames := make([]string, 0, len(labels))
+ for labelName := range labels {
+ labelNames = append(labelNames, labelName)
+ }
+ sort.Strings(labelNames)
+
+ sum := hashNew()
+ for _, labelName := range labelNames {
+ sum = hashAdd(sum, labelName)
+ sum = hashAddByte(sum, SeparatorByte)
+ sum = hashAdd(sum, labels[labelName])
+ sum = hashAddByte(sum, SeparatorByte)
+ }
+ return sum
+}
+
+// labelSetToFingerprint works exactly as LabelsToSignature but takes a LabelSet as
+// parameter (rather than a label map) and returns a Fingerprint.
+func labelSetToFingerprint(ls LabelSet) Fingerprint {
+ if len(ls) == 0 {
+ return Fingerprint(emptyLabelSignature)
+ }
+
+ labelNames := make(LabelNames, 0, len(ls))
+ for labelName := range ls {
+ labelNames = append(labelNames, labelName)
+ }
+ sort.Sort(labelNames)
+
+ sum := hashNew()
+ for _, labelName := range labelNames {
+ sum = hashAdd(sum, string(labelName))
+ sum = hashAddByte(sum, SeparatorByte)
+ sum = hashAdd(sum, string(ls[labelName]))
+ sum = hashAddByte(sum, SeparatorByte)
+ }
+ return Fingerprint(sum)
+}
+
+// labelSetToFastFingerprint works similar to labelSetToFingerprint but uses a
+// faster and less allocation-heavy hash function, which is more susceptible to
+// create hash collisions. Therefore, collision detection should be applied.
+func labelSetToFastFingerprint(ls LabelSet) Fingerprint {
+ if len(ls) == 0 {
+ return Fingerprint(emptyLabelSignature)
+ }
+
+ var result uint64
+ for labelName, labelValue := range ls {
+ sum := hashNew()
+ sum = hashAdd(sum, string(labelName))
+ sum = hashAddByte(sum, SeparatorByte)
+ sum = hashAdd(sum, string(labelValue))
+ result ^= sum
+ }
+ return Fingerprint(result)
+}
+
+// SignatureForLabels works like LabelsToSignature but takes a Metric as
+// parameter (rather than a label map) and only includes the labels with the
+// specified LabelNames into the signature calculation. The labels passed in
+// will be sorted by this function.
+func SignatureForLabels(m Metric, labels ...LabelName) uint64 {
+ if len(labels) == 0 {
+ return emptyLabelSignature
+ }
+
+ sort.Sort(LabelNames(labels))
+
+ sum := hashNew()
+ for _, label := range labels {
+ sum = hashAdd(sum, string(label))
+ sum = hashAddByte(sum, SeparatorByte)
+ sum = hashAdd(sum, string(m[label]))
+ sum = hashAddByte(sum, SeparatorByte)
+ }
+ return sum
+}
+
+// SignatureWithoutLabels works like LabelsToSignature but takes a Metric as
+// parameter (rather than a label map) and excludes the labels with any of the
+// specified LabelNames from the signature calculation.
+func SignatureWithoutLabels(m Metric, labels map[LabelName]struct{}) uint64 {
+ if len(m) == 0 {
+ return emptyLabelSignature
+ }
+
+ labelNames := make(LabelNames, 0, len(m))
+ for labelName := range m {
+ if _, exclude := labels[labelName]; !exclude {
+ labelNames = append(labelNames, labelName)
+ }
+ }
+ if len(labelNames) == 0 {
+ return emptyLabelSignature
+ }
+ sort.Sort(labelNames)
+
+ sum := hashNew()
+ for _, labelName := range labelNames {
+ sum = hashAdd(sum, string(labelName))
+ sum = hashAddByte(sum, SeparatorByte)
+ sum = hashAdd(sum, string(m[labelName]))
+ sum = hashAddByte(sum, SeparatorByte)
+ }
+ return sum
+}
diff --git a/vendor/github.com/prometheus/common/model/silence.go b/vendor/github.com/prometheus/common/model/silence.go
new file mode 100644
index 00000000000..7538e299774
--- /dev/null
+++ b/vendor/github.com/prometheus/common/model/silence.go
@@ -0,0 +1,106 @@
+// Copyright 2015 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "encoding/json"
+ "fmt"
+ "regexp"
+ "time"
+)
+
+// Matcher describes a matches the value of a given label.
+type Matcher struct {
+ Name LabelName `json:"name"`
+ Value string `json:"value"`
+ IsRegex bool `json:"isRegex"`
+}
+
+func (m *Matcher) UnmarshalJSON(b []byte) error {
+ type plain Matcher
+ if err := json.Unmarshal(b, (*plain)(m)); err != nil {
+ return err
+ }
+
+ if len(m.Name) == 0 {
+ return fmt.Errorf("label name in matcher must not be empty")
+ }
+ if m.IsRegex {
+ if _, err := regexp.Compile(m.Value); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Validate returns true iff all fields of the matcher have valid values.
+func (m *Matcher) Validate() error {
+ if !m.Name.IsValid() {
+ return fmt.Errorf("invalid name %q", m.Name)
+ }
+ if m.IsRegex {
+ if _, err := regexp.Compile(m.Value); err != nil {
+ return fmt.Errorf("invalid regular expression %q", m.Value)
+ }
+ } else if !LabelValue(m.Value).IsValid() || len(m.Value) == 0 {
+ return fmt.Errorf("invalid value %q", m.Value)
+ }
+ return nil
+}
+
+// Silence defines the representation of a silence definiton
+// in the Prometheus eco-system.
+type Silence struct {
+ ID uint64 `json:"id,omitempty"`
+
+ Matchers []*Matcher `json:"matchers"`
+
+ StartsAt time.Time `json:"startsAt"`
+ EndsAt time.Time `json:"endsAt"`
+
+ CreatedAt time.Time `json:"createdAt,omitempty"`
+ CreatedBy string `json:"createdBy"`
+ Comment string `json:"comment,omitempty"`
+}
+
+// Validate returns true iff all fields of the silence have valid values.
+func (s *Silence) Validate() error {
+ if len(s.Matchers) == 0 {
+ return fmt.Errorf("at least one matcher required")
+ }
+ for _, m := range s.Matchers {
+ if err := m.Validate(); err != nil {
+ return fmt.Errorf("invalid matcher: %s", err)
+ }
+ }
+ if s.StartsAt.IsZero() {
+ return fmt.Errorf("start time missing")
+ }
+ if s.EndsAt.IsZero() {
+ return fmt.Errorf("end time missing")
+ }
+ if s.EndsAt.Before(s.StartsAt) {
+ return fmt.Errorf("start time must be before end time")
+ }
+ if s.CreatedBy == "" {
+ return fmt.Errorf("creator information missing")
+ }
+ if s.Comment == "" {
+ return fmt.Errorf("comment missing")
+ }
+ if s.CreatedAt.IsZero() {
+ return fmt.Errorf("creation timestamp missing")
+ }
+ return nil
+}
diff --git a/vendor/github.com/prometheus/common/model/time.go b/vendor/github.com/prometheus/common/model/time.go
new file mode 100644
index 00000000000..548968aebe6
--- /dev/null
+++ b/vendor/github.com/prometheus/common/model/time.go
@@ -0,0 +1,249 @@
+// Copyright 2013 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "fmt"
+ "math"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+)
+
+const (
+ // MinimumTick is the minimum supported time resolution. This has to be
+ // at least time.Second in order for the code below to work.
+ minimumTick = time.Millisecond
+ // second is the Time duration equivalent to one second.
+ second = int64(time.Second / minimumTick)
+ // The number of nanoseconds per minimum tick.
+ nanosPerTick = int64(minimumTick / time.Nanosecond)
+
+ // Earliest is the earliest Time representable. Handy for
+ // initializing a high watermark.
+ Earliest = Time(math.MinInt64)
+ // Latest is the latest Time representable. Handy for initializing
+ // a low watermark.
+ Latest = Time(math.MaxInt64)
+)
+
+// Time is the number of milliseconds since the epoch
+// (1970-01-01 00:00 UTC) excluding leap seconds.
+type Time int64
+
+// Interval describes and interval between two timestamps.
+type Interval struct {
+ Start, End Time
+}
+
+// Now returns the current time as a Time.
+func Now() Time {
+ return TimeFromUnixNano(time.Now().UnixNano())
+}
+
+// TimeFromUnix returns the Time equivalent to the Unix Time t
+// provided in seconds.
+func TimeFromUnix(t int64) Time {
+ return Time(t * second)
+}
+
+// TimeFromUnixNano returns the Time equivalent to the Unix Time
+// t provided in nanoseconds.
+func TimeFromUnixNano(t int64) Time {
+ return Time(t / nanosPerTick)
+}
+
+// Equal reports whether two Times represent the same instant.
+func (t Time) Equal(o Time) bool {
+ return t == o
+}
+
+// Before reports whether the Time t is before o.
+func (t Time) Before(o Time) bool {
+ return t < o
+}
+
+// After reports whether the Time t is after o.
+func (t Time) After(o Time) bool {
+ return t > o
+}
+
+// Add returns the Time t + d.
+func (t Time) Add(d time.Duration) Time {
+ return t + Time(d/minimumTick)
+}
+
+// Sub returns the Duration t - o.
+func (t Time) Sub(o Time) time.Duration {
+ return time.Duration(t-o) * minimumTick
+}
+
+// Time returns the time.Time representation of t.
+func (t Time) Time() time.Time {
+ return time.Unix(int64(t)/second, (int64(t)%second)*nanosPerTick)
+}
+
+// Unix returns t as a Unix time, the number of seconds elapsed
+// since January 1, 1970 UTC.
+func (t Time) Unix() int64 {
+ return int64(t) / second
+}
+
+// UnixNano returns t as a Unix time, the number of nanoseconds elapsed
+// since January 1, 1970 UTC.
+func (t Time) UnixNano() int64 {
+ return int64(t) * nanosPerTick
+}
+
+// The number of digits after the dot.
+var dotPrecision = int(math.Log10(float64(second)))
+
+// String returns a string representation of the Time.
+func (t Time) String() string {
+ return strconv.FormatFloat(float64(t)/float64(second), 'f', -1, 64)
+}
+
+// MarshalJSON implements the json.Marshaler interface.
+func (t Time) MarshalJSON() ([]byte, error) {
+ return []byte(t.String()), nil
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (t *Time) UnmarshalJSON(b []byte) error {
+ p := strings.Split(string(b), ".")
+ switch len(p) {
+ case 1:
+ v, err := strconv.ParseInt(string(p[0]), 10, 64)
+ if err != nil {
+ return err
+ }
+ *t = Time(v * second)
+
+ case 2:
+ v, err := strconv.ParseInt(string(p[0]), 10, 64)
+ if err != nil {
+ return err
+ }
+ v *= second
+
+ prec := dotPrecision - len(p[1])
+ if prec < 0 {
+ p[1] = p[1][:dotPrecision]
+ } else if prec > 0 {
+ p[1] = p[1] + strings.Repeat("0", prec)
+ }
+
+ va, err := strconv.ParseInt(p[1], 10, 32)
+ if err != nil {
+ return err
+ }
+
+ *t = Time(v + va)
+
+ default:
+ return fmt.Errorf("invalid time %q", string(b))
+ }
+ return nil
+}
+
+// Duration wraps time.Duration. It is used to parse the custom duration format
+// from YAML.
+// This type should not propagate beyond the scope of input/output processing.
+type Duration time.Duration
+
+var durationRE = regexp.MustCompile("^([0-9]+)(y|w|d|h|m|s|ms)$")
+
+// StringToDuration parses a string into a time.Duration, assuming that a year
+// always has 365d, a week always has 7d, and a day always has 24h.
+func ParseDuration(durationStr string) (Duration, error) {
+ matches := durationRE.FindStringSubmatch(durationStr)
+ if len(matches) != 3 {
+ return 0, fmt.Errorf("not a valid duration string: %q", durationStr)
+ }
+ var (
+ n, _ = strconv.Atoi(matches[1])
+ dur = time.Duration(n) * time.Millisecond
+ )
+ switch unit := matches[2]; unit {
+ case "y":
+ dur *= 1000 * 60 * 60 * 24 * 365
+ case "w":
+ dur *= 1000 * 60 * 60 * 24 * 7
+ case "d":
+ dur *= 1000 * 60 * 60 * 24
+ case "h":
+ dur *= 1000 * 60 * 60
+ case "m":
+ dur *= 1000 * 60
+ case "s":
+ dur *= 1000
+ case "ms":
+ // Value already correct
+ default:
+ return 0, fmt.Errorf("invalid time unit in duration string: %q", unit)
+ }
+ return Duration(dur), nil
+}
+
+func (d Duration) String() string {
+ var (
+ ms = int64(time.Duration(d) / time.Millisecond)
+ unit = "ms"
+ )
+ factors := map[string]int64{
+ "y": 1000 * 60 * 60 * 24 * 365,
+ "w": 1000 * 60 * 60 * 24 * 7,
+ "d": 1000 * 60 * 60 * 24,
+ "h": 1000 * 60 * 60,
+ "m": 1000 * 60,
+ "s": 1000,
+ "ms": 1,
+ }
+
+ switch int64(0) {
+ case ms % factors["y"]:
+ unit = "y"
+ case ms % factors["w"]:
+ unit = "w"
+ case ms % factors["d"]:
+ unit = "d"
+ case ms % factors["h"]:
+ unit = "h"
+ case ms % factors["m"]:
+ unit = "m"
+ case ms % factors["s"]:
+ unit = "s"
+ }
+ return fmt.Sprintf("%v%v", ms/factors[unit], unit)
+}
+
+// MarshalYAML implements the yaml.Marshaler interface.
+func (d Duration) MarshalYAML() (interface{}, error) {
+ return d.String(), nil
+}
+
+// UnmarshalYAML implements the yaml.Unmarshaler interface.
+func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error {
+ var s string
+ if err := unmarshal(&s); err != nil {
+ return err
+ }
+ dur, err := ParseDuration(s)
+ if err != nil {
+ return err
+ }
+ *d = dur
+ return nil
+}
diff --git a/vendor/github.com/prometheus/common/model/value.go b/vendor/github.com/prometheus/common/model/value.go
new file mode 100644
index 00000000000..dbf5d10e431
--- /dev/null
+++ b/vendor/github.com/prometheus/common/model/value.go
@@ -0,0 +1,403 @@
+// Copyright 2013 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "encoding/json"
+ "fmt"
+ "math"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+// A SampleValue is a representation of a value for a given sample at a given
+// time.
+type SampleValue float64
+
+// MarshalJSON implements json.Marshaler.
+func (v SampleValue) MarshalJSON() ([]byte, error) {
+ return json.Marshal(v.String())
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (v *SampleValue) UnmarshalJSON(b []byte) error {
+ if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' {
+ return fmt.Errorf("sample value must be a quoted string")
+ }
+ f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64)
+ if err != nil {
+ return err
+ }
+ *v = SampleValue(f)
+ return nil
+}
+
+// Equal returns true if the value of v and o is equal or if both are NaN. Note
+// that v==o is false if both are NaN. If you want the conventional float
+// behavior, use == to compare two SampleValues.
+func (v SampleValue) Equal(o SampleValue) bool {
+ if v == o {
+ return true
+ }
+ return math.IsNaN(float64(v)) && math.IsNaN(float64(o))
+}
+
+func (v SampleValue) String() string {
+ return strconv.FormatFloat(float64(v), 'f', -1, 64)
+}
+
+// SamplePair pairs a SampleValue with a Timestamp.
+type SamplePair struct {
+ Timestamp Time
+ Value SampleValue
+}
+
+// MarshalJSON implements json.Marshaler.
+func (s SamplePair) MarshalJSON() ([]byte, error) {
+ t, err := json.Marshal(s.Timestamp)
+ if err != nil {
+ return nil, err
+ }
+ v, err := json.Marshal(s.Value)
+ if err != nil {
+ return nil, err
+ }
+ return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (s *SamplePair) UnmarshalJSON(b []byte) error {
+ v := [...]json.Unmarshaler{&s.Timestamp, &s.Value}
+ return json.Unmarshal(b, &v)
+}
+
+// Equal returns true if this SamplePair and o have equal Values and equal
+// Timestamps. The sematics of Value equality is defined by SampleValue.Equal.
+func (s *SamplePair) Equal(o *SamplePair) bool {
+ return s == o || (s.Value.Equal(o.Value) && s.Timestamp.Equal(o.Timestamp))
+}
+
+func (s SamplePair) String() string {
+ return fmt.Sprintf("%s @[%s]", s.Value, s.Timestamp)
+}
+
+// Sample is a sample pair associated with a metric.
+type Sample struct {
+ Metric Metric `json:"metric"`
+ Value SampleValue `json:"value"`
+ Timestamp Time `json:"timestamp"`
+}
+
+// Equal compares first the metrics, then the timestamp, then the value. The
+// sematics of value equality is defined by SampleValue.Equal.
+func (s *Sample) Equal(o *Sample) bool {
+ if s == o {
+ return true
+ }
+
+ if !s.Metric.Equal(o.Metric) {
+ return false
+ }
+ if !s.Timestamp.Equal(o.Timestamp) {
+ return false
+ }
+ if s.Value.Equal(o.Value) {
+ return false
+ }
+
+ return true
+}
+
+func (s Sample) String() string {
+ return fmt.Sprintf("%s => %s", s.Metric, SamplePair{
+ Timestamp: s.Timestamp,
+ Value: s.Value,
+ })
+}
+
+// MarshalJSON implements json.Marshaler.
+func (s Sample) MarshalJSON() ([]byte, error) {
+ v := struct {
+ Metric Metric `json:"metric"`
+ Value SamplePair `json:"value"`
+ }{
+ Metric: s.Metric,
+ Value: SamplePair{
+ Timestamp: s.Timestamp,
+ Value: s.Value,
+ },
+ }
+
+ return json.Marshal(&v)
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (s *Sample) UnmarshalJSON(b []byte) error {
+ v := struct {
+ Metric Metric `json:"metric"`
+ Value SamplePair `json:"value"`
+ }{
+ Metric: s.Metric,
+ Value: SamplePair{
+ Timestamp: s.Timestamp,
+ Value: s.Value,
+ },
+ }
+
+ if err := json.Unmarshal(b, &v); err != nil {
+ return err
+ }
+
+ s.Metric = v.Metric
+ s.Timestamp = v.Value.Timestamp
+ s.Value = v.Value.Value
+
+ return nil
+}
+
+// Samples is a sortable Sample slice. It implements sort.Interface.
+type Samples []*Sample
+
+func (s Samples) Len() int {
+ return len(s)
+}
+
+// Less compares first the metrics, then the timestamp.
+func (s Samples) Less(i, j int) bool {
+ switch {
+ case s[i].Metric.Before(s[j].Metric):
+ return true
+ case s[j].Metric.Before(s[i].Metric):
+ return false
+ case s[i].Timestamp.Before(s[j].Timestamp):
+ return true
+ default:
+ return false
+ }
+}
+
+func (s Samples) Swap(i, j int) {
+ s[i], s[j] = s[j], s[i]
+}
+
+// Equal compares two sets of samples and returns true if they are equal.
+func (s Samples) Equal(o Samples) bool {
+ if len(s) != len(o) {
+ return false
+ }
+
+ for i, sample := range s {
+ if !sample.Equal(o[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+// SampleStream is a stream of Values belonging to an attached COWMetric.
+type SampleStream struct {
+ Metric Metric `json:"metric"`
+ Values []SamplePair `json:"values"`
+}
+
+func (ss SampleStream) String() string {
+ vals := make([]string, len(ss.Values))
+ for i, v := range ss.Values {
+ vals[i] = v.String()
+ }
+ return fmt.Sprintf("%s =>\n%s", ss.Metric, strings.Join(vals, "\n"))
+}
+
+// Value is a generic interface for values resulting from a query evaluation.
+type Value interface {
+ Type() ValueType
+ String() string
+}
+
+func (Matrix) Type() ValueType { return ValMatrix }
+func (Vector) Type() ValueType { return ValVector }
+func (*Scalar) Type() ValueType { return ValScalar }
+func (*String) Type() ValueType { return ValString }
+
+type ValueType int
+
+const (
+ ValNone ValueType = iota
+ ValScalar
+ ValVector
+ ValMatrix
+ ValString
+)
+
+// MarshalJSON implements json.Marshaler.
+func (et ValueType) MarshalJSON() ([]byte, error) {
+ return json.Marshal(et.String())
+}
+
+func (et *ValueType) UnmarshalJSON(b []byte) error {
+ var s string
+ if err := json.Unmarshal(b, &s); err != nil {
+ return err
+ }
+ switch s {
+ case "
":
+ *et = ValNone
+ case "scalar":
+ *et = ValScalar
+ case "vector":
+ *et = ValVector
+ case "matrix":
+ *et = ValMatrix
+ case "string":
+ *et = ValString
+ default:
+ return fmt.Errorf("unknown value type %q", s)
+ }
+ return nil
+}
+
+func (e ValueType) String() string {
+ switch e {
+ case ValNone:
+ return ""
+ case ValScalar:
+ return "scalar"
+ case ValVector:
+ return "vector"
+ case ValMatrix:
+ return "matrix"
+ case ValString:
+ return "string"
+ }
+ panic("ValueType.String: unhandled value type")
+}
+
+// Scalar is a scalar value evaluated at the set timestamp.
+type Scalar struct {
+ Value SampleValue `json:"value"`
+ Timestamp Time `json:"timestamp"`
+}
+
+func (s Scalar) String() string {
+ return fmt.Sprintf("scalar: %v @[%v]", s.Value, s.Timestamp)
+}
+
+// MarshalJSON implements json.Marshaler.
+func (s Scalar) MarshalJSON() ([]byte, error) {
+ v := strconv.FormatFloat(float64(s.Value), 'f', -1, 64)
+ return json.Marshal([...]interface{}{s.Timestamp, string(v)})
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (s *Scalar) UnmarshalJSON(b []byte) error {
+ var f string
+ v := [...]interface{}{&s.Timestamp, &f}
+
+ if err := json.Unmarshal(b, &v); err != nil {
+ return err
+ }
+
+ value, err := strconv.ParseFloat(f, 64)
+ if err != nil {
+ return fmt.Errorf("error parsing sample value: %s", err)
+ }
+ s.Value = SampleValue(value)
+ return nil
+}
+
+// String is a string value evaluated at the set timestamp.
+type String struct {
+ Value string `json:"value"`
+ Timestamp Time `json:"timestamp"`
+}
+
+func (s *String) String() string {
+ return s.Value
+}
+
+// MarshalJSON implements json.Marshaler.
+func (s String) MarshalJSON() ([]byte, error) {
+ return json.Marshal([]interface{}{s.Timestamp, s.Value})
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (s *String) UnmarshalJSON(b []byte) error {
+ v := [...]interface{}{&s.Timestamp, &s.Value}
+ return json.Unmarshal(b, &v)
+}
+
+// Vector is basically only an alias for Samples, but the
+// contract is that in a Vector, all Samples have the same timestamp.
+type Vector []*Sample
+
+func (vec Vector) String() string {
+ entries := make([]string, len(vec))
+ for i, s := range vec {
+ entries[i] = s.String()
+ }
+ return strings.Join(entries, "\n")
+}
+
+func (vec Vector) Len() int { return len(vec) }
+func (vec Vector) Swap(i, j int) { vec[i], vec[j] = vec[j], vec[i] }
+
+// Less compares first the metrics, then the timestamp.
+func (vec Vector) Less(i, j int) bool {
+ switch {
+ case vec[i].Metric.Before(vec[j].Metric):
+ return true
+ case vec[j].Metric.Before(vec[i].Metric):
+ return false
+ case vec[i].Timestamp.Before(vec[j].Timestamp):
+ return true
+ default:
+ return false
+ }
+}
+
+// Equal compares two sets of samples and returns true if they are equal.
+func (vec Vector) Equal(o Vector) bool {
+ if len(vec) != len(o) {
+ return false
+ }
+
+ for i, sample := range vec {
+ if !sample.Equal(o[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+// Matrix is a list of time series.
+type Matrix []*SampleStream
+
+func (m Matrix) Len() int { return len(m) }
+func (m Matrix) Less(i, j int) bool { return m[i].Metric.Before(m[j].Metric) }
+func (m Matrix) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
+
+func (mat Matrix) String() string {
+ matCp := make(Matrix, len(mat))
+ copy(matCp, mat)
+ sort.Sort(matCp)
+
+ strs := make([]string, len(matCp))
+
+ for i, ss := range matCp {
+ strs[i] = ss.String()
+ }
+
+ return strings.Join(strs, "\n")
+}
diff --git a/vendor/golang.org/x/net/LICENSE b/vendor/golang.org/x/net/LICENSE
new file mode 100644
index 00000000000..6a66aea5eaf
--- /dev/null
+++ b/vendor/golang.org/x/net/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/golang.org/x/net/PATENTS b/vendor/golang.org/x/net/PATENTS
new file mode 100644
index 00000000000..733099041f8
--- /dev/null
+++ b/vendor/golang.org/x/net/PATENTS
@@ -0,0 +1,22 @@
+Additional IP Rights Grant (Patents)
+
+"This implementation" means the copyrightable works distributed by
+Google as part of the Go project.
+
+Google hereby grants to You a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable (except as stated in this section)
+patent license to make, have made, use, offer to sell, sell, import,
+transfer and otherwise run, modify and propagate the contents of this
+implementation of Go, where such license applies only to those patent
+claims, both currently owned or controlled by Google and acquired in
+the future, licensable by Google that are necessarily infringed by this
+implementation of Go. This grant does not include claims that would be
+infringed only as a consequence of further modification of this
+implementation. If you or your agent or exclusive licensee institute or
+order or agree to the institution of patent litigation against any
+entity (including a cross-claim or counterclaim in a lawsuit) alleging
+that this implementation of Go or any code incorporated within this
+implementation of Go constitutes direct or contributory patent
+infringement, or inducement of patent infringement, then any patent
+rights granted to you under this License for this implementation of Go
+shall terminate as of the date such litigation is filed.
diff --git a/vendor/golang.org/x/net/context/ctxhttp/ctxhttp.go b/vendor/golang.org/x/net/context/ctxhttp/ctxhttp.go
new file mode 100644
index 00000000000..606cf1f9726
--- /dev/null
+++ b/vendor/golang.org/x/net/context/ctxhttp/ctxhttp.go
@@ -0,0 +1,74 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.7
+
+// Package ctxhttp provides helper functions for performing context-aware HTTP requests.
+package ctxhttp // import "golang.org/x/net/context/ctxhttp"
+
+import (
+ "io"
+ "net/http"
+ "net/url"
+ "strings"
+
+ "golang.org/x/net/context"
+)
+
+// Do sends an HTTP request with the provided http.Client and returns
+// an HTTP response.
+//
+// If the client is nil, http.DefaultClient is used.
+//
+// The provided ctx must be non-nil. If it is canceled or times out,
+// ctx.Err() will be returned.
+func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
+ if client == nil {
+ client = http.DefaultClient
+ }
+ resp, err := client.Do(req.WithContext(ctx))
+ // If we got an error, and the context has been canceled,
+ // the context's error is probably more useful.
+ if err != nil {
+ select {
+ case <-ctx.Done():
+ err = ctx.Err()
+ default:
+ }
+ }
+ return resp, err
+}
+
+// Get issues a GET request via the Do function.
+func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
+ req, err := http.NewRequest("GET", url, nil)
+ if err != nil {
+ return nil, err
+ }
+ return Do(ctx, client, req)
+}
+
+// Head issues a HEAD request via the Do function.
+func Head(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
+ req, err := http.NewRequest("HEAD", url, nil)
+ if err != nil {
+ return nil, err
+ }
+ return Do(ctx, client, req)
+}
+
+// Post issues a POST request via the Do function.
+func Post(ctx context.Context, client *http.Client, url string, bodyType string, body io.Reader) (*http.Response, error) {
+ req, err := http.NewRequest("POST", url, body)
+ if err != nil {
+ return nil, err
+ }
+ req.Header.Set("Content-Type", bodyType)
+ return Do(ctx, client, req)
+}
+
+// PostForm issues a POST request via the Do function.
+func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) {
+ return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
+}
diff --git a/vendor/golang.org/x/net/context/ctxhttp/ctxhttp_pre17.go b/vendor/golang.org/x/net/context/ctxhttp/ctxhttp_pre17.go
new file mode 100644
index 00000000000..926870cc23f
--- /dev/null
+++ b/vendor/golang.org/x/net/context/ctxhttp/ctxhttp_pre17.go
@@ -0,0 +1,147 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.7
+
+package ctxhttp // import "golang.org/x/net/context/ctxhttp"
+
+import (
+ "io"
+ "net/http"
+ "net/url"
+ "strings"
+
+ "golang.org/x/net/context"
+)
+
+func nop() {}
+
+var (
+ testHookContextDoneBeforeHeaders = nop
+ testHookDoReturned = nop
+ testHookDidBodyClose = nop
+)
+
+// Do sends an HTTP request with the provided http.Client and returns an HTTP response.
+// If the client is nil, http.DefaultClient is used.
+// If the context is canceled or times out, ctx.Err() will be returned.
+func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
+ if client == nil {
+ client = http.DefaultClient
+ }
+
+ // TODO(djd): Respect any existing value of req.Cancel.
+ cancel := make(chan struct{})
+ req.Cancel = cancel
+
+ type responseAndError struct {
+ resp *http.Response
+ err error
+ }
+ result := make(chan responseAndError, 1)
+
+ // Make local copies of test hooks closed over by goroutines below.
+ // Prevents data races in tests.
+ testHookDoReturned := testHookDoReturned
+ testHookDidBodyClose := testHookDidBodyClose
+
+ go func() {
+ resp, err := client.Do(req)
+ testHookDoReturned()
+ result <- responseAndError{resp, err}
+ }()
+
+ var resp *http.Response
+
+ select {
+ case <-ctx.Done():
+ testHookContextDoneBeforeHeaders()
+ close(cancel)
+ // Clean up after the goroutine calling client.Do:
+ go func() {
+ if r := <-result; r.resp != nil {
+ testHookDidBodyClose()
+ r.resp.Body.Close()
+ }
+ }()
+ return nil, ctx.Err()
+ case r := <-result:
+ var err error
+ resp, err = r.resp, r.err
+ if err != nil {
+ return resp, err
+ }
+ }
+
+ c := make(chan struct{})
+ go func() {
+ select {
+ case <-ctx.Done():
+ close(cancel)
+ case <-c:
+ // The response's Body is closed.
+ }
+ }()
+ resp.Body = ¬ifyingReader{resp.Body, c}
+
+ return resp, nil
+}
+
+// Get issues a GET request via the Do function.
+func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
+ req, err := http.NewRequest("GET", url, nil)
+ if err != nil {
+ return nil, err
+ }
+ return Do(ctx, client, req)
+}
+
+// Head issues a HEAD request via the Do function.
+func Head(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
+ req, err := http.NewRequest("HEAD", url, nil)
+ if err != nil {
+ return nil, err
+ }
+ return Do(ctx, client, req)
+}
+
+// Post issues a POST request via the Do function.
+func Post(ctx context.Context, client *http.Client, url string, bodyType string, body io.Reader) (*http.Response, error) {
+ req, err := http.NewRequest("POST", url, body)
+ if err != nil {
+ return nil, err
+ }
+ req.Header.Set("Content-Type", bodyType)
+ return Do(ctx, client, req)
+}
+
+// PostForm issues a POST request via the Do function.
+func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) {
+ return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
+}
+
+// notifyingReader is an io.ReadCloser that closes the notify channel after
+// Close is called or a Read fails on the underlying ReadCloser.
+type notifyingReader struct {
+ io.ReadCloser
+ notify chan<- struct{}
+}
+
+func (r *notifyingReader) Read(p []byte) (int, error) {
+ n, err := r.ReadCloser.Read(p)
+ if err != nil && r.notify != nil {
+ close(r.notify)
+ r.notify = nil
+ }
+ return n, err
+}
+
+func (r *notifyingReader) Close() error {
+ err := r.ReadCloser.Close()
+ if r.notify != nil {
+ close(r.notify)
+ r.notify = nil
+ }
+ return err
+}
diff --git a/vendor/golang.org/x/sync/LICENSE b/vendor/golang.org/x/sync/LICENSE
new file mode 100644
index 00000000000..6a66aea5eaf
--- /dev/null
+++ b/vendor/golang.org/x/sync/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/golang.org/x/sync/PATENTS b/vendor/golang.org/x/sync/PATENTS
new file mode 100644
index 00000000000..733099041f8
--- /dev/null
+++ b/vendor/golang.org/x/sync/PATENTS
@@ -0,0 +1,22 @@
+Additional IP Rights Grant (Patents)
+
+"This implementation" means the copyrightable works distributed by
+Google as part of the Go project.
+
+Google hereby grants to You a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable (except as stated in this section)
+patent license to make, have made, use, offer to sell, sell, import,
+transfer and otherwise run, modify and propagate the contents of this
+implementation of Go, where such license applies only to those patent
+claims, both currently owned or controlled by Google and acquired in
+the future, licensable by Google that are necessarily infringed by this
+implementation of Go. This grant does not include claims that would be
+infringed only as a consequence of further modification of this
+implementation. If you or your agent or exclusive licensee institute or
+order or agree to the institution of patent litigation against any
+entity (including a cross-claim or counterclaim in a lawsuit) alleging
+that this implementation of Go or any code incorporated within this
+implementation of Go constitutes direct or contributory patent
+infringement, or inducement of patent infringement, then any patent
+rights granted to you under this License for this implementation of Go
+shall terminate as of the date such litigation is filed.
diff --git a/vendor/golang.org/x/sync/errgroup/errgroup.go b/vendor/golang.org/x/sync/errgroup/errgroup.go
new file mode 100644
index 00000000000..533438d91c1
--- /dev/null
+++ b/vendor/golang.org/x/sync/errgroup/errgroup.go
@@ -0,0 +1,67 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package errgroup provides synchronization, error propagation, and Context
+// cancelation for groups of goroutines working on subtasks of a common task.
+package errgroup
+
+import (
+ "sync"
+
+ "golang.org/x/net/context"
+)
+
+// A Group is a collection of goroutines working on subtasks that are part of
+// the same overall task.
+//
+// A zero Group is valid and does not cancel on error.
+type Group struct {
+ cancel func()
+
+ wg sync.WaitGroup
+
+ errOnce sync.Once
+ err error
+}
+
+// WithContext returns a new Group and an associated Context derived from ctx.
+//
+// The derived Context is canceled the first time a function passed to Go
+// returns a non-nil error or the first time Wait returns, whichever occurs
+// first.
+func WithContext(ctx context.Context) (*Group, context.Context) {
+ ctx, cancel := context.WithCancel(ctx)
+ return &Group{cancel: cancel}, ctx
+}
+
+// Wait blocks until all function calls from the Go method have returned, then
+// returns the first non-nil error (if any) from them.
+func (g *Group) Wait() error {
+ g.wg.Wait()
+ if g.cancel != nil {
+ g.cancel()
+ }
+ return g.err
+}
+
+// Go calls the given function in a new goroutine.
+//
+// The first call to return a non-nil error cancels the group; its error will be
+// returned by Wait.
+func (g *Group) Go(f func() error) {
+ g.wg.Add(1)
+
+ go func() {
+ defer g.wg.Done()
+
+ if err := f(); err != nil {
+ g.errOnce.Do(func() {
+ g.err = err
+ if g.cancel != nil {
+ g.cancel()
+ }
+ })
+ }
+ }()
+}
diff --git a/vendor/gopkg.in/guregu/null.v3/LICENSE b/vendor/gopkg.in/guregu/null.v3/LICENSE
new file mode 100644
index 00000000000..69062b45b16
--- /dev/null
+++ b/vendor/gopkg.in/guregu/null.v3/LICENSE
@@ -0,0 +1,10 @@
+Copyright (c) 2014, Greg Roseberry
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/vendor/gopkg.in/guregu/null.v3/README.md b/vendor/gopkg.in/guregu/null.v3/README.md
new file mode 100644
index 00000000000..62bf48be93e
--- /dev/null
+++ b/vendor/gopkg.in/guregu/null.v3/README.md
@@ -0,0 +1,75 @@
+## null [](https://godoc.org/github.com/guregu/null) [](http://gocover.io/github.com/guregu/null)
+`import "gopkg.in/guregu/null.v3"`
+
+null is a library with reasonable options for dealing with nullable SQL and JSON values
+
+There are two packages: `null` and its subpackage `zero`.
+
+Types in `null` will only be considered null on null input, and will JSON encode to `null`. If you need zero and null be considered separate values, use these.
+
+Types in `zero` are treated like zero values in Go: blank string input will produce a null `zero.String`, and null Strings will JSON encode to `""`. Zero values of these types will be considered null to SQL. If you need zero and null treated the same, use these.
+
+All types implement `sql.Scanner` and `driver.Valuer`, so you can use this library in place of `sql.NullXXX`. All types also implement: `encoding.TextMarshaler`, `encoding.TextUnmarshaler`, `json.Marshaler`, and `json.Unmarshaler`.
+
+### null package
+
+`import "gopkg.in/guregu/null.v3"`
+
+#### null.String
+Nullable string.
+
+Marshals to JSON null if SQL source data is null. Zero (blank) input will not produce a null String. Can unmarshal from `sql.NullString` JSON input or string input.
+
+#### null.Int
+Nullable int64.
+
+Marshals to JSON null if SQL source data is null. Zero input will not produce a null Int. Can unmarshal from `sql.NullInt64` JSON input.
+
+#### null.Float
+Nullable float64.
+
+Marshals to JSON null if SQL source data is null. Zero input will not produce a null Float. Can unmarshal from `sql.NullFloat64` JSON input.
+
+#### null.Bool
+Nullable bool.
+
+Marshals to JSON null if SQL source data is null. False input will not produce a null Bool. Can unmarshal from `sql.NullBool` JSON input.
+
+#### null.Time
+
+Marshals to JSON null if SQL source data is null. Uses `time.Time`'s marshaler. Can unmarshal from `pq.NullTime` and similar JSON input.
+
+### zero package
+
+`import "gopkg.in/guregu/null.v3/zero"`
+
+#### zero.String
+Nullable string.
+
+Will marshal to a blank string if null. Blank string input produces a null String. Null values and zero values are considered equivalent. Can unmarshal from `sql.NullString` JSON input.
+
+#### zero.Int
+Nullable int64.
+
+Will marshal to 0 if null. 0 produces a null Int. Null values and zero values are considered equivalent. Can unmarshal from `sql.NullInt64` JSON input.
+
+#### zero.Float
+Nullable float64.
+
+Will marshal to 0 if null. 0.0 produces a null Float. Null values and zero values are considered equivalent. Can unmarshal from `sql.NullFloat64` JSON input.
+
+#### zero.Bool
+Nullable bool.
+
+Will marshal to false if null. `false` produces a null Float. Null values and zero values are considered equivalent. Can unmarshal from `sql.NullBool` JSON input.
+
+#### zero.Time
+
+Will marshal to the zero time if null. Uses `time.Time`'s marshaler. Can unmarshal from `pq.NullTime` and similar JSON input.
+
+
+### Bugs
+`json`'s `",omitempty"` struct tag does not work correctly right now. It will never omit a null or empty String. This might be [fixed eventually](https://github.com/golang/go/issues/4357).
+
+### License
+BSD
diff --git a/vendor/gopkg.in/guregu/null.v3/bool.go b/vendor/gopkg.in/guregu/null.v3/bool.go
new file mode 100644
index 00000000000..1ec13961bfe
--- /dev/null
+++ b/vendor/gopkg.in/guregu/null.v3/bool.go
@@ -0,0 +1,129 @@
+package null
+
+import (
+ "database/sql"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "reflect"
+)
+
+// Bool is a nullable bool.
+// It does not consider false values to be null.
+// It will decode to null, not false, if null.
+type Bool struct {
+ sql.NullBool
+}
+
+// NewBool creates a new Bool
+func NewBool(b bool, valid bool) Bool {
+ return Bool{
+ NullBool: sql.NullBool{
+ Bool: b,
+ Valid: valid,
+ },
+ }
+}
+
+// BoolFrom creates a new Bool that will always be valid.
+func BoolFrom(b bool) Bool {
+ return NewBool(b, true)
+}
+
+// BoolFromPtr creates a new Bool that will be null if f is nil.
+func BoolFromPtr(b *bool) Bool {
+ if b == nil {
+ return NewBool(false, false)
+ }
+ return NewBool(*b, true)
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+// It supports number and null input.
+// 0 will not be considered a null Bool.
+// It also supports unmarshalling a sql.NullBool.
+func (b *Bool) UnmarshalJSON(data []byte) error {
+ var err error
+ var v interface{}
+ if err = json.Unmarshal(data, &v); err != nil {
+ return err
+ }
+ switch x := v.(type) {
+ case bool:
+ b.Bool = x
+ case map[string]interface{}:
+ err = json.Unmarshal(data, &b.NullBool)
+ case nil:
+ b.Valid = false
+ return nil
+ default:
+ err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Bool", reflect.TypeOf(v).Name())
+ }
+ b.Valid = err == nil
+ return err
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+// It will unmarshal to a null Bool if the input is a blank or not an integer.
+// It will return an error if the input is not an integer, blank, or "null".
+func (b *Bool) UnmarshalText(text []byte) error {
+ str := string(text)
+ switch str {
+ case "", "null":
+ b.Valid = false
+ return nil
+ case "true":
+ b.Bool = true
+ case "false":
+ b.Bool = false
+ default:
+ b.Valid = false
+ return errors.New("invalid input:" + str)
+ }
+ b.Valid = true
+ return nil
+}
+
+// MarshalJSON implements json.Marshaler.
+// It will encode null if this Bool is null.
+func (b Bool) MarshalJSON() ([]byte, error) {
+ if !b.Valid {
+ return []byte("null"), nil
+ }
+ if !b.Bool {
+ return []byte("false"), nil
+ }
+ return []byte("true"), nil
+}
+
+// MarshalText implements encoding.TextMarshaler.
+// It will encode a blank string if this Bool is null.
+func (b Bool) MarshalText() ([]byte, error) {
+ if !b.Valid {
+ return []byte{}, nil
+ }
+ if !b.Bool {
+ return []byte("false"), nil
+ }
+ return []byte("true"), nil
+}
+
+// SetValid changes this Bool's value and also sets it to be non-null.
+func (b *Bool) SetValid(v bool) {
+ b.Bool = v
+ b.Valid = true
+}
+
+// Ptr returns a pointer to this Bool's value, or a nil pointer if this Bool is null.
+func (b Bool) Ptr() *bool {
+ if !b.Valid {
+ return nil
+ }
+ return &b.Bool
+}
+
+// IsZero returns true for invalid Bools, for future omitempty support (Go 1.4?)
+// A non-null Bool with a 0 value will not be considered zero.
+func (b Bool) IsZero() bool {
+ return !b.Valid
+}
diff --git a/vendor/gopkg.in/guregu/null.v3/float.go b/vendor/gopkg.in/guregu/null.v3/float.go
new file mode 100644
index 00000000000..1f57b959ab7
--- /dev/null
+++ b/vendor/gopkg.in/guregu/null.v3/float.go
@@ -0,0 +1,117 @@
+package null
+
+import (
+ "database/sql"
+ "encoding/json"
+ "fmt"
+ "reflect"
+ "strconv"
+)
+
+// Float is a nullable float64.
+// It does not consider zero values to be null.
+// It will decode to null, not zero, if null.
+type Float struct {
+ sql.NullFloat64
+}
+
+// NewFloat creates a new Float
+func NewFloat(f float64, valid bool) Float {
+ return Float{
+ NullFloat64: sql.NullFloat64{
+ Float64: f,
+ Valid: valid,
+ },
+ }
+}
+
+// FloatFrom creates a new Float that will always be valid.
+func FloatFrom(f float64) Float {
+ return NewFloat(f, true)
+}
+
+// FloatFromPtr creates a new Float that be null if f is nil.
+func FloatFromPtr(f *float64) Float {
+ if f == nil {
+ return NewFloat(0, false)
+ }
+ return NewFloat(*f, true)
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+// It supports number and null input.
+// 0 will not be considered a null Float.
+// It also supports unmarshalling a sql.NullFloat64.
+func (f *Float) UnmarshalJSON(data []byte) error {
+ var err error
+ var v interface{}
+ if err = json.Unmarshal(data, &v); err != nil {
+ return err
+ }
+ switch x := v.(type) {
+ case float64:
+ f.Float64 = float64(x)
+ case map[string]interface{}:
+ err = json.Unmarshal(data, &f.NullFloat64)
+ case nil:
+ f.Valid = false
+ return nil
+ default:
+ err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Float", reflect.TypeOf(v).Name())
+ }
+ f.Valid = err == nil
+ return err
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+// It will unmarshal to a null Float if the input is a blank or not an integer.
+// It will return an error if the input is not an integer, blank, or "null".
+func (f *Float) UnmarshalText(text []byte) error {
+ str := string(text)
+ if str == "" || str == "null" {
+ f.Valid = false
+ return nil
+ }
+ var err error
+ f.Float64, err = strconv.ParseFloat(string(text), 64)
+ f.Valid = err == nil
+ return err
+}
+
+// MarshalJSON implements json.Marshaler.
+// It will encode null if this Float is null.
+func (f Float) MarshalJSON() ([]byte, error) {
+ if !f.Valid {
+ return []byte("null"), nil
+ }
+ return []byte(strconv.FormatFloat(f.Float64, 'f', -1, 64)), nil
+}
+
+// MarshalText implements encoding.TextMarshaler.
+// It will encode a blank string if this Float is null.
+func (f Float) MarshalText() ([]byte, error) {
+ if !f.Valid {
+ return []byte{}, nil
+ }
+ return []byte(strconv.FormatFloat(f.Float64, 'f', -1, 64)), nil
+}
+
+// SetValid changes this Float's value and also sets it to be non-null.
+func (f *Float) SetValid(n float64) {
+ f.Float64 = n
+ f.Valid = true
+}
+
+// Ptr returns a pointer to this Float's value, or a nil pointer if this Float is null.
+func (f Float) Ptr() *float64 {
+ if !f.Valid {
+ return nil
+ }
+ return &f.Float64
+}
+
+// IsZero returns true for invalid Floats, for future omitempty support (Go 1.4?)
+// A non-null Float with a 0 value will not be considered zero.
+func (f Float) IsZero() bool {
+ return !f.Valid
+}
diff --git a/vendor/gopkg.in/guregu/null.v3/int.go b/vendor/gopkg.in/guregu/null.v3/int.go
new file mode 100644
index 00000000000..981d17b09aa
--- /dev/null
+++ b/vendor/gopkg.in/guregu/null.v3/int.go
@@ -0,0 +1,118 @@
+package null
+
+import (
+ "database/sql"
+ "encoding/json"
+ "fmt"
+ "reflect"
+ "strconv"
+)
+
+// Int is an nullable int64.
+// It does not consider zero values to be null.
+// It will decode to null, not zero, if null.
+type Int struct {
+ sql.NullInt64
+}
+
+// NewInt creates a new Int
+func NewInt(i int64, valid bool) Int {
+ return Int{
+ NullInt64: sql.NullInt64{
+ Int64: i,
+ Valid: valid,
+ },
+ }
+}
+
+// IntFrom creates a new Int that will always be valid.
+func IntFrom(i int64) Int {
+ return NewInt(i, true)
+}
+
+// IntFromPtr creates a new Int that be null if i is nil.
+func IntFromPtr(i *int64) Int {
+ if i == nil {
+ return NewInt(0, false)
+ }
+ return NewInt(*i, true)
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+// It supports number and null input.
+// 0 will not be considered a null Int.
+// It also supports unmarshalling a sql.NullInt64.
+func (i *Int) UnmarshalJSON(data []byte) error {
+ var err error
+ var v interface{}
+ if err = json.Unmarshal(data, &v); err != nil {
+ return err
+ }
+ switch v.(type) {
+ case float64:
+ // Unmarshal again, directly to int64, to avoid intermediate float64
+ err = json.Unmarshal(data, &i.Int64)
+ case map[string]interface{}:
+ err = json.Unmarshal(data, &i.NullInt64)
+ case nil:
+ i.Valid = false
+ return nil
+ default:
+ err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Int", reflect.TypeOf(v).Name())
+ }
+ i.Valid = err == nil
+ return err
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+// It will unmarshal to a null Int if the input is a blank or not an integer.
+// It will return an error if the input is not an integer, blank, or "null".
+func (i *Int) UnmarshalText(text []byte) error {
+ str := string(text)
+ if str == "" || str == "null" {
+ i.Valid = false
+ return nil
+ }
+ var err error
+ i.Int64, err = strconv.ParseInt(string(text), 10, 64)
+ i.Valid = err == nil
+ return err
+}
+
+// MarshalJSON implements json.Marshaler.
+// It will encode null if this Int is null.
+func (i Int) MarshalJSON() ([]byte, error) {
+ if !i.Valid {
+ return []byte("null"), nil
+ }
+ return []byte(strconv.FormatInt(i.Int64, 10)), nil
+}
+
+// MarshalText implements encoding.TextMarshaler.
+// It will encode a blank string if this Int is null.
+func (i Int) MarshalText() ([]byte, error) {
+ if !i.Valid {
+ return []byte{}, nil
+ }
+ return []byte(strconv.FormatInt(i.Int64, 10)), nil
+}
+
+// SetValid changes this Int's value and also sets it to be non-null.
+func (i *Int) SetValid(n int64) {
+ i.Int64 = n
+ i.Valid = true
+}
+
+// Ptr returns a pointer to this Int's value, or a nil pointer if this Int is null.
+func (i Int) Ptr() *int64 {
+ if !i.Valid {
+ return nil
+ }
+ return &i.Int64
+}
+
+// IsZero returns true for invalid Ints, for future omitempty support (Go 1.4?)
+// A non-null Int with a 0 value will not be considered zero.
+func (i Int) IsZero() bool {
+ return !i.Valid
+}
diff --git a/vendor/gopkg.in/guregu/null.v3/string.go b/vendor/gopkg.in/guregu/null.v3/string.go
new file mode 100644
index 00000000000..554aac820e3
--- /dev/null
+++ b/vendor/gopkg.in/guregu/null.v3/string.go
@@ -0,0 +1,110 @@
+// Package null contains SQL types that consider zero input and null input as separate values,
+// with convenient support for JSON and text marshaling.
+// Types in this package will always encode to their null value if null.
+// Use the zero subpackage if you want zero values and null to be treated the same.
+package null
+
+import (
+ "database/sql"
+ "encoding/json"
+ "fmt"
+ "reflect"
+)
+
+// String is a nullable string. It supports SQL and JSON serialization.
+// It will marshal to null if null. Blank string input will be considered null.
+type String struct {
+ sql.NullString
+}
+
+// StringFrom creates a new String that will never be blank.
+func StringFrom(s string) String {
+ return NewString(s, true)
+}
+
+// StringFromPtr creates a new String that be null if s is nil.
+func StringFromPtr(s *string) String {
+ if s == nil {
+ return NewString("", false)
+ }
+ return NewString(*s, true)
+}
+
+// NewString creates a new String
+func NewString(s string, valid bool) String {
+ return String{
+ NullString: sql.NullString{
+ String: s,
+ Valid: valid,
+ },
+ }
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+// It supports string and null input. Blank string input does not produce a null String.
+// It also supports unmarshalling a sql.NullString.
+func (s *String) UnmarshalJSON(data []byte) error {
+ var err error
+ var v interface{}
+ if err = json.Unmarshal(data, &v); err != nil {
+ return err
+ }
+ switch x := v.(type) {
+ case string:
+ s.String = x
+ case map[string]interface{}:
+ err = json.Unmarshal(data, &s.NullString)
+ case nil:
+ s.Valid = false
+ return nil
+ default:
+ err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.String", reflect.TypeOf(v).Name())
+ }
+ s.Valid = err == nil
+ return err
+}
+
+// MarshalJSON implements json.Marshaler.
+// It will encode null if this String is null.
+func (s String) MarshalJSON() ([]byte, error) {
+ if !s.Valid {
+ return []byte("null"), nil
+ }
+ return json.Marshal(s.String)
+}
+
+// MarshalText implements encoding.TextMarshaler.
+// It will encode a blank string when this String is null.
+func (s String) MarshalText() ([]byte, error) {
+ if !s.Valid {
+ return []byte{}, nil
+ }
+ return []byte(s.String), nil
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+// It will unmarshal to a null String if the input is a blank string.
+func (s *String) UnmarshalText(text []byte) error {
+ s.String = string(text)
+ s.Valid = s.String != ""
+ return nil
+}
+
+// SetValid changes this String's value and also sets it to be non-null.
+func (s *String) SetValid(v string) {
+ s.String = v
+ s.Valid = true
+}
+
+// Ptr returns a pointer to this String's value, or a nil pointer if this String is null.
+func (s String) Ptr() *string {
+ if !s.Valid {
+ return nil
+ }
+ return &s.String
+}
+
+// IsZero returns true for null strings, for potential future omitempty support.
+func (s String) IsZero() bool {
+ return !s.Valid
+}
diff --git a/vendor/gopkg.in/guregu/null.v3/time.go b/vendor/gopkg.in/guregu/null.v3/time.go
new file mode 100644
index 00000000000..a4d843920b4
--- /dev/null
+++ b/vendor/gopkg.in/guregu/null.v3/time.go
@@ -0,0 +1,135 @@
+package null
+
+import (
+ "database/sql/driver"
+ "encoding/json"
+ "fmt"
+ "reflect"
+ "time"
+)
+
+// Time is a nullable time.Time. It supports SQL and JSON serialization.
+// It will marshal to null if null.
+type Time struct {
+ Time time.Time
+ Valid bool
+}
+
+// Scan implements the Scanner interface.
+func (t *Time) Scan(value interface{}) error {
+ var err error
+ switch x := value.(type) {
+ case time.Time:
+ t.Time = x
+ case nil:
+ t.Valid = false
+ return nil
+ default:
+ err = fmt.Errorf("null: cannot scan type %T into null.Time: %v", value, value)
+ }
+ t.Valid = err == nil
+ return err
+}
+
+// Value implements the driver Valuer interface.
+func (t Time) Value() (driver.Value, error) {
+ if !t.Valid {
+ return nil, nil
+ }
+ return t.Time, nil
+}
+
+// NewTime creates a new Time.
+func NewTime(t time.Time, valid bool) Time {
+ return Time{
+ Time: t,
+ Valid: valid,
+ }
+}
+
+// TimeFrom creates a new Time that will always be valid.
+func TimeFrom(t time.Time) Time {
+ return NewTime(t, true)
+}
+
+// TimeFromPtr creates a new Time that will be null if t is nil.
+func TimeFromPtr(t *time.Time) Time {
+ if t == nil {
+ return NewTime(time.Time{}, false)
+ }
+ return NewTime(*t, true)
+}
+
+// MarshalJSON implements json.Marshaler.
+// It will encode null if this time is null.
+func (t Time) MarshalJSON() ([]byte, error) {
+ if !t.Valid {
+ return []byte("null"), nil
+ }
+ return t.Time.MarshalJSON()
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+// It supports string, object (e.g. pq.NullTime and friends)
+// and null input.
+func (t *Time) UnmarshalJSON(data []byte) error {
+ var err error
+ var v interface{}
+ if err = json.Unmarshal(data, &v); err != nil {
+ return err
+ }
+ switch x := v.(type) {
+ case string:
+ err = t.Time.UnmarshalJSON(data)
+ case map[string]interface{}:
+ ti, tiOK := x["Time"].(string)
+ valid, validOK := x["Valid"].(bool)
+ if !tiOK || !validOK {
+ return fmt.Errorf(`json: unmarshalling object into Go value of type null.Time requires key "Time" to be of type string and key "Valid" to be of type bool; found %T and %T, respectively`, x["Time"], x["Valid"])
+ }
+ err = t.Time.UnmarshalText([]byte(ti))
+ t.Valid = valid
+ return err
+ case nil:
+ t.Valid = false
+ return nil
+ default:
+ err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Time", reflect.TypeOf(v).Name())
+ }
+ t.Valid = err == nil
+ return err
+}
+
+func (t Time) MarshalText() ([]byte, error) {
+ if !t.Valid {
+ return []byte("null"), nil
+ }
+ return t.Time.MarshalText()
+}
+
+func (t *Time) UnmarshalText(text []byte) error {
+ str := string(text)
+ if str == "" || str == "null" {
+ t.Valid = false
+ return nil
+ }
+ if err := t.Time.UnmarshalText(text); err != nil {
+ return err
+ }
+ t.Valid = true
+ return nil
+}
+
+// SetValid changes this Time's value and sets it to be non-null.
+func (t *Time) SetValid(v time.Time) {
+ t.Time = v
+ t.Valid = true
+}
+
+// Ptr returns a pointer to this Time's value, or a nil pointer if this Time is null.
+func (t Time) Ptr() *time.Time {
+ if !t.Valid {
+ return nil
+ }
+ return &t.Time
+}
diff --git a/vendor/phantomjs/render.js b/vendor/phantomjs/render.js
index 3e10ee852f9..2f62bfce955 100644
--- a/vendor/phantomjs/render.js
+++ b/vendor/phantomjs/render.js
@@ -12,17 +12,17 @@
params[parts[1]] = parts[2];
});
- var usage = "url= png= width= height= cookiename= sessionid= domain=";
+ var usage = "url= png= width= height= renderKey=";
- if (!params.url || !params.png || !params.cookiename || ! params.sessionid || !params.domain) {
+ if (!params.url || !params.png || !params.renderKey || !params.domain) {
console.log(usage);
phantom.exit();
}
phantom.addCookie({
- 'name': params.cookiename,
- 'value': params.sessionid,
- 'domain': params.domain
+ 'name': 'renderKey',
+ 'value': params.renderKey,
+ 'domain': 'localhost',
});
page.viewportSize = {
diff --git a/vendor/vendor.json b/vendor/vendor.json
index aea55c068fb..1c5a8962a49 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -2,6 +2,18 @@
"comment": "",
"ignore": "test",
"package": [
+ {
+ "checksumSHA1": "SMUvX2B8eoFd9wnPofwBKlN6btE=",
+ "path": "github.com/prometheus/client_golang/api/prometheus",
+ "revision": "5636dc67ae776adf5590da7349e70fbb9559972d",
+ "revisionTime": "2016-09-16T18:03:40Z"
+ },
+ {
+ "checksumSHA1": "Jx0GXl5hGnO25s3ryyvtdWHdCpw=",
+ "path": "github.com/prometheus/common/model",
+ "revision": "9a94032291f2192936512bab367bc45e77990d6a",
+ "revisionTime": "2016-09-17T18:44:01Z"
+ },
{
"checksumSHA1": "6AYg4fjEvFuAVN3wHakGApjhZAM=",
"path": "github.com/smartystreets/assertions",
@@ -37,6 +49,24 @@
"path": "github.com/smartystreets/goconvey/convey/reporting",
"revision": "5db88ed452e937f2fd557de6f4f1af7f2eabed0b",
"revisionTime": "2016-08-23T18:01:44Z"
+ },
+ {
+ "checksumSHA1": "WHc3uByvGaMcnSoI21fhzYgbOgg=",
+ "path": "golang.org/x/net/context/ctxhttp",
+ "revision": "71a035914f99bb58fe82eac0f1289f10963d876c",
+ "revisionTime": "2016-09-12T21:59:12Z"
+ },
+ {
+ "checksumSHA1": "S0DP7Pn7sZUmXc55IzZnNvERu6s=",
+ "path": "golang.org/x/sync/errgroup",
+ "revision": "316e794f7b5e3df4e95175a45a5fb8b12f85cb4f",
+ "revisionTime": "2016-07-15T18:54:39Z"
+ },
+ {
+ "checksumSHA1": "PoHLopxwkiXxa3uVhezeq/qJ/Vo=",
+ "path": "gopkg.in/guregu/null.v3",
+ "revision": "41961cea0328defc5f95c1c473f89ebf0d1813f6",
+ "revisionTime": "2016-02-28T00:53:16Z"
}
],
"rootPath": "github.com/grafana/grafana"