Browse Source

Merge remote-tracking branch 'upstream/master'

Alin Sinpalean 8 years ago
parent
commit
7a7837f47e
42 changed files with 239 additions and 127 deletions
  1. 9 3
      docs/sources/features/datasources/graphite.md
  2. 3 1
      docs/sources/features/datasources/influxdb.md
  3. 2 6
      docs/sources/features/panels/graph.md
  4. 1 1
      docs/sources/installation/windows.md
  5. 22 0
      docs/sources/reference/playlist.md
  6. 0 4
      package.json
  7. 2 0
      pkg/api/cloudwatch/metrics.go
  8. 1 1
      pkg/api/metrics.go
  9. 6 0
      pkg/services/alerting/conditions/query.go
  10. 3 2
      pkg/util/encryption.go
  11. 12 0
      public/app/core/components/code_editor/code_editor.ts
  12. 6 6
      public/app/core/components/code_editor/mode-prometheus.js
  13. 10 1
      public/app/core/services/alert_srv.ts
  14. 8 2
      public/app/core/services/backend_srv.ts
  15. 3 6
      public/app/core/utils/kbn.js
  16. 1 1
      public/app/features/dashboard/dashboard_srv.ts
  17. 2 0
      public/app/features/panel/query_ctrl.ts
  18. 2 5
      public/app/features/plugins/ds_edit_ctrl.ts
  19. 8 3
      public/app/features/plugins/partials/ds_edit.html
  20. 1 1
      public/app/plugins/datasource/cloudwatch/datasource.js
  21. 4 4
      public/app/plugins/datasource/elasticsearch/datasource.js
  22. 1 1
      public/app/plugins/datasource/graphite/datasource.ts
  23. 5 0
      public/app/plugins/datasource/graphite/specs/query_ctrl_specs.ts
  24. 3 3
      public/app/plugins/datasource/influxdb/datasource.ts
  25. 1 1
      public/app/plugins/datasource/influxdb/partials/query.editor.html
  26. 6 2
      public/app/plugins/datasource/influxdb/specs/query_ctrl_specs.ts
  27. 3 3
      public/app/plugins/datasource/mysql/datasource.ts
  28. 1 1
      public/app/plugins/datasource/opentsdb/datasource.js
  29. 5 1
      public/app/plugins/datasource/opentsdb/specs/query-ctrl-specs.ts
  30. 1 1
      public/app/plugins/datasource/prometheus/datasource.ts
  31. 1 1
      public/app/plugins/datasource/prometheus/partials/query.editor.html
  32. 27 0
      public/app/plugins/datasource/prometheus/specs/completer_specs.ts
  33. 1 1
      public/app/plugins/panel/text/editor.html
  34. 12 12
      public/sass/_variables.dark.scss
  35. 9 15
      public/sass/_variables.light.scss
  36. 9 9
      public/sass/base/_type.scss
  37. 34 22
      public/sass/components/_alerts.scss
  38. 5 1
      public/sass/components/_code_editor.scss
  39. 1 1
      public/sass/components/_drop.scss
  40. 1 1
      public/sass/pages/_dashboard.scss
  41. 1 1
      public/test/core/utils/kbn_specs.js
  42. 6 3
      public/views/index.html

+ 9 - 3
docs/sources/features/datasources/graphite.md

@@ -41,7 +41,9 @@ Proxy access means that the Grafana backend will proxy all requests from the bro
 Click the ``Select metric`` link to start navigating the metric space. One you start you can continue using the mouse
 or keyboard arrow keys. You can select a wildcard and still continue.
 
-![](/img/docs/animated_gifs/graphite_query1.gif)
+{{< docs-imagebox img="/img/docs/v45/graphite_query1_still.png" class="docs-image--center"
+                  animated-gif="/img/docs/v45/graphite_query1.gif" >}}
+
 
 ### Functions
 
@@ -50,13 +52,17 @@ a function is selected it will be added and your focus will be in the text box o
 a parameter just click on it and it will turn into a text box. To delete a function click the function name followed
 by the x icon.
 
-![](/img/docs/animated_gifs/graphite_query2.gif)
+{{< docs-imagebox img="/img/docs/v45/graphite_query2_still.png" class="docs-image--center"
+                  animated-gif="/img/docs/v45/graphite_query2.gif" >}}
+
 
 ### Optional parameters
 
 Some functions like aliasByNode support an optional second argument. To add this parameter specify for example 3,-2 as the first parameter and the function editor will adapt and move the -2 to a second parameter. To remove the second optional parameter just click on it and leave it blank and the editor will remove it.
 
-![](/img/docs/animated_gifs/func_editor_optional_params.gif)
+{{< docs-imagebox img="/img/docs/v45/graphite_query3_still.png" class="docs-image--center"
+                  animated-gif="/img/docs/v45/graphite_query3.gif" >}}
+
 
 ### Nested Queries
 

+ 3 - 1
docs/sources/features/datasources/influxdb.md

@@ -41,7 +41,9 @@ mode is also more secure as the username & password will never reach the browser
 
 ## Query Editor
 
-![](/assets/img/blog/v2.6/influxdb_editor_v3.gif)
+{{< docs-imagebox img="/img/docs/v45/influxdb_query_still.png" class="docs-image--center"
+                  animated-gif="/img/docs/v45/influxdb_query.gif" >}}
+
 
 You find the InfluxDB editor in the metrics tab in Graph or Singlestat panel's edit mode. You enter edit mode by clicking the
 panel title, then edit. The editor allows you to select metrics and tags.

+ 2 - 6
docs/sources/features/panels/graph.md

@@ -50,15 +50,11 @@ populate the template variable to a desired value from the link.
 The metrics tab defines what series data and sources to render.  Each datasource provides different
 options.
 
-## Axes & Grid
+## Axes
 
 ![](/img/docs/v43/graph_axes_grid_options.png)
 
-The Axes & Grid tab controls the display of axes, grids and legend.
-
-### Axes
-
-The ``Left Y`` and ``Right Y`` can be customized using:
+The Axes tab controls the display of axes, grids and legend.  The ``Left Y`` and ``Right Y`` can be customized using:
 
 - ``Unit`` - The display unit for the Y value
 - ``Grid Max`` - The maximum Y value. (default auto)

+ 1 - 1
docs/sources/installation/windows.md

@@ -27,7 +27,7 @@ this folder to anywhere you want Grafana to run from.  Go into the
 
 The default Grafana port is `3000`, this port requires extra permissions
 on windows. Edit `custom.ini` and uncomment the `http_port`
-configuration option and change it to something like `8080` or similar.
+configuration option (`;` is the comment character in ini files) and change it to something like `8080` or similar.
 That port should not require extra Windows privileges.
 
 Start Grafana by executing `grafana-server.exe`, preferably from the

+ 22 - 0
docs/sources/reference/playlist.md

@@ -43,3 +43,25 @@ Playlists can also be manually controlled utilizing the Playlist controls at the
 Click the stop button to stop the Playlist, and exit to the current Dashboard.
 Click the next button to advance to the next Dashboard in the Playlist.
 Click the back button to rewind to the previous Dashboard in the Playlist.
+
+## TV or Kiosk Mode
+
+In TV mode the top navbar, row & panel controls will all fade to transparent.
+
+This happens automatically after one minute of user inactivity but can also be toggled manually
+with the `d v` sequence shortcut. Any mouse movement or keyboard action will
+restore navbar & controls.
+
+Another feature is the kiosk mode - in kiosk mode the navbar is completely hidden/removed from view. This can be enabled with the `d k`
+shortcut.
+
+To put a playlist into kiosk mode, use the `d k` shortcut after the playlist has started. The same shortcut will toggle the playlist out of kiosk mode.
+
+### Linking to the Playlist in Kiosk Mode
+
+If you want to create a link to the playlist with kiosk mode enabled:
+
+1. Copy the Start Url (by right clicking on the Play button and choosing Copy link address).
+2. Add the `?kiosk` parameter to the url.
+
+For example, to open the first playlist on the Grafana Play site in kiosk mode: [http://play.grafana.org/playlists/play/1?kiosk](http://play.grafana.org/playlists/play/1?kiosk)

+ 0 - 4
package.json

@@ -53,10 +53,6 @@
     "systemjs": "0.19.41",
     "zone.js": "^0.7.2"
   },
-  "engines": {
-    "node": "4.x",
-    "npm": "2.14.x"
-  },
   "scripts": {
     "build": "./node_modules/grunt-cli/bin/grunt",
     "test": "./node_modules/grunt-cli/bin/grunt test",

+ 2 - 0
pkg/api/cloudwatch/metrics.go

@@ -78,6 +78,7 @@ func init() {
 		"AWS/Lambda":           {"Invocations", "Errors", "Duration", "Throttles", "IteratorAge"},
 		"AWS/Logs":             {"IncomingBytes", "IncomingLogEvents", "ForwardedBytes", "ForwardedLogEvents", "DeliveryErrors", "DeliveryThrottling"},
 		"AWS/ML":               {"PredictCount", "PredictFailureCount"},
+		"AWS/NATGateway":       {"PacketsOutToDestination", "PacketsOutToSource", "PacketsInFromSource", "PacketsInFromDestination", "BytesOutToDestination", "BytesOutToSource", "BytesInFromSource", "BytesInFromDestination", "ErrorPortAllocation", "ActiveConnectionCount", "ConnectionAttemptCount", "ConnectionEstablishedCount", "IdleTimeoutCount", "PacketsDropCount"},
 		"AWS/OpsWorks":         {"cpu_idle", "cpu_nice", "cpu_system", "cpu_user", "cpu_waitio", "load_1", "load_5", "load_15", "memory_buffers", "memory_cached", "memory_free", "memory_swap", "memory_total", "memory_used", "procs"},
 		"AWS/Redshift":         {"CPUUtilization", "DatabaseConnections", "HealthStatus", "MaintenanceMode", "NetworkReceiveThroughput", "NetworkTransmitThroughput", "PercentageDiskSpaceUsed", "ReadIOPS", "ReadLatency", "ReadThroughput", "WriteIOPS", "WriteLatency", "WriteThroughput"},
 		"AWS/RDS":              {"ActiveTransactions", "AuroraBinlogReplicaLag", "AuroraReplicaLag", "AuroraReplicaLagMaximum", "AuroraReplicaLagMinimum", "BinLogDiskUsage", "BlockedTransactions", "BufferCacheHitRatio", "CommitLatency", "CommitThroughput", "BinLogDiskUsage", "CPUCreditBalance", "CPUCreditUsage", "CPUUtilization", "DatabaseConnections", "DDLLatency", "DDLThroughput", "Deadlocks", "DeleteLatency", "DeleteThroughput", "DiskQueueDepth", "DMLLatency", "DMLThroughput", "EngineUptime", "FailedSqlStatements", "FreeableMemory", "FreeLocalStorage", "FreeStorageSpace", "InsertLatency", "InsertThroughput", "LoginFailures", "NetworkReceiveThroughput", "NetworkTransmitThroughput", "NetworkThroughput", "Queries", "ReadIOPS", "ReadLatency", "ReadThroughput", "ReplicaLag", "ResultSetCacheHitRatio", "SelectLatency", "SelectThroughput", "SwapUsage", "TotalConnections", "UpdateLatency", "UpdateThroughput", "VolumeBytesUsed", "VolumeReadIOPS", "VolumeWriteIOPS", "WriteIOPS", "WriteLatency", "WriteThroughput"},
@@ -122,6 +123,7 @@ func init() {
 		"AWS/Lambda":           {"FunctionName", "Resource", "Version", "Alias"},
 		"AWS/Logs":             {"LogGroupName", "DestinationType", "FilterName"},
 		"AWS/ML":               {"MLModelId", "RequestMode"},
+		"AWS/NATGateway":       {"NatGatewayId"},
 		"AWS/OpsWorks":         {"StackId", "LayerId", "InstanceId"},
 		"AWS/Redshift":         {"NodeID", "ClusterIdentifier"},
 		"AWS/RDS":              {"DBInstanceIdentifier", "DBClusterIdentifier", "DatabaseClass", "EngineName", "Role"},

+ 1 - 1
pkg/api/metrics.go

@@ -29,7 +29,7 @@ func QueryMetrics(c *middleware.Context, reqDto dtos.MetricRequest) Response {
 		return ApiError(400, "Query missing datasourceId", nil)
 	}
 
-	dsQuery := models.GetDataSourceByIdQuery{Id: dsId}
+	dsQuery := models.GetDataSourceByIdQuery{Id: dsId, OrgId: c.OrgId}
 	if err := bus.Dispatch(&dsQuery); err != nil {
 		return ApiError(500, "failed to fetch data source", err)
 	}

+ 6 - 0
pkg/services/alerting/conditions/query.go

@@ -5,6 +5,8 @@ import (
 	"strings"
 	"time"
 
+	gocontext "context"
+
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/components/null"
 	"github.com/grafana/grafana/pkg/components/simplejson"
@@ -112,6 +114,10 @@ func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *
 
 	resp, err := c.HandleRequest(context.Ctx, req)
 	if err != nil {
+		if err == gocontext.DeadlineExceeded {
+			return nil, fmt.Errorf("Alert execution exceeded the timeout")
+		}
+
 		return nil, fmt.Errorf("tsdb.HandleRequest() error %v", err)
 	}
 

+ 3 - 2
pkg/util/encryption.go

@@ -27,12 +27,13 @@ func Decrypt(payload []byte, secret string) ([]byte, error) {
 	}
 	iv := payload[saltLength : saltLength+aes.BlockSize]
 	payload = payload[saltLength+aes.BlockSize:]
+	payloadDst := make([]byte, len(payload))
 
 	stream := cipher.NewCFBDecrypter(block, iv)
 
 	// XORKeyStream can work in-place if the two arguments are the same.
-	stream.XORKeyStream(payload, payload)
-	return payload, nil
+	stream.XORKeyStream(payloadDst, payload)
+	return payloadDst, nil
 }
 
 func Encrypt(payload []byte, secret string) ([]byte, error) {

+ 12 - 0
public/app/core/components/code_editor/code_editor.ts

@@ -111,6 +111,17 @@ function link(scope, elem, attrs) {
   let textarea = elem.find("textarea");
   textarea.addClass('gf-form-input');
 
+  if (scope.codeEditorFocus) {
+    setTimeout(function () {
+      textarea.focus();
+      var domEl = textarea[0];
+      if (domEl.setSelectionRange) {
+        var pos = textarea.val().length * 2;
+        domEl.setSelectionRange(pos, pos);
+      }
+    }, 100);
+  }
+
   // Event handlers
   editorSession.on('change', (e) => {
     scope.$apply(() => {
@@ -199,6 +210,7 @@ export function codeEditorDirective() {
     template: editorTemplate,
     scope: {
       content: "=",
+      codeEditorFocus: "<",
       onChange: "&",
       getCompleter: "&"
     },

+ 6 - 6
public/app/core/components/code_editor/mode-prometheus.js

@@ -65,20 +65,20 @@ var PrometheusHighlightRules = function() {
       regex : "\\s+"
     } ],
     "start-label-matcher" : [ {
-      token : "label.name",
+      token : "keyword",
       regex : '[a-zA-Z_][a-zA-Z0-9_]*'
     }, {
-      token : "label.matching_operator",
-      regex : '=|!=|=~|!~'
+      token : "keyword.operator",
+      regex : '=~|=|!~|!='
     }, {
-      token : "label.value",
+      token : "string",
       regex : '"[^"]*"|\'[^\']*\''
     }, {
-      token : "label.matching_delimiter",
+      token : "punctuation.operator",
       regex : ",",
       push  : 'start-label-matcher'
     }, {
-      token : "label.matching_end",
+      token : "paren.rparen",
       regex : "}",
       next  : "start"
     } ]

+ 10 - 1
public/app/core/services/alert_srv.ts

@@ -16,7 +16,7 @@ export class AlertSrv {
 
   init() {
     this.$rootScope.onAppEvent('alert-error', (e, alert) => {
-      this.set(alert[0], alert[1], 'error', 7000);
+      this.set(alert[0], alert[1], 'error', 12000);
     }, this.$rootScope);
 
     this.$rootScope.onAppEvent('alert-warning', (e, alert) => {
@@ -33,6 +33,14 @@ export class AlertSrv {
     appEvents.on('confirm-modal', this.showConfirmModal.bind(this));
   }
 
+  getIconForSeverity(severity) {
+    switch (severity) {
+      case 'success': return 'fa fa-check';
+      case 'error': return 'fa fa-exclamation-triangle';
+      default: return 'fa fa-exclamation';
+    }
+  }
+
   set(title, text, severity, timeout) {
     if (_.isObject(text)) {
       console.log('alert error', text);
@@ -45,6 +53,7 @@ export class AlertSrv {
       title: title || '',
       text: text || '',
       severity: severity || 'info',
+      icon: this.getIconForSeverity(severity)
     };
 
     var newAlertJson = angular.toJson(newAlert);

+ 8 - 2
public/app/core/services/backend_srv.ts

@@ -64,7 +64,13 @@ export class BackendSrv {
     }
 
     if (data.message) {
-      this.alertSrv.set("Problem!", data.message, data.severity, 10000);
+      let description = "";
+      let message = data.message;
+      if (message.length > 80) {
+        description = message;
+        message = "Error";
+      }
+      this.alertSrv.set(message, description, data.severity, 10000);
     }
 
     throw data;
@@ -97,7 +103,7 @@ export class BackendSrv {
       return results.data;
     }, err => {
       // handle unauthorized
-      if (err.status === 401 && firstAttempt) {
+      if (err.status === 401 && this.contextSrv.user.isSignedIn && firstAttempt) {
         return this.loginPing().then(() => {
           options.retry = 1;
           return this.request(options);

+ 3 - 6
public/app/core/utils/kbn.js

@@ -88,19 +88,16 @@ function($, _) {
       // 9h
       case (interval < 32400000):
         return 21600000;  // 6h
-      // 24h
+      // 1d
       case (interval < 86400000):
         return 43200000;  // 12h
-      // 48h
-      case (interval < 172800000):
-        return 86400000;  // 24h
       // 1w
       case (interval < 604800000):
-        return 86400000;  // 24h
+        return 86400000;  // 1d
       // 3w
       case (interval < 1814400000):
         return 604800000; // 1w
-      // 2y
+      // 6w
       case (interval < 3628800000):
         return 2592000000; // 30d
       default:

+ 1 - 1
public/app/features/dashboard/dashboard_srv.ts

@@ -83,7 +83,7 @@ export class DashboardSrv {
     }
 
     this.$rootScope.appEvent('dashboard-saved', this.dash);
-    this.$rootScope.appEvent('alert-success', ['Dashboard saved', 'Saved as ' + clone.title]);
+    this.$rootScope.appEvent('alert-success', ['Dashboard saved']);
   }
 
   save(clone, options) {

+ 2 - 0
public/app/features/panel/query_ctrl.ts

@@ -10,9 +10,11 @@ export class QueryCtrl {
   panel: any;
   hasRawMode: boolean;
   error: string;
+  isLastQuery: boolean;
 
   constructor(public $scope, private $injector) {
     this.panel = this.panelCtrl.panel;
+    this.isLastQuery = _.indexOf(this.panel.targets, this.target) === (this.panel.targets.length - 1);
   }
 
   refresh() {

+ 2 - 5
public/app/features/plugins/ds_edit_ctrl.ts

@@ -126,21 +126,18 @@ export class DataSourceEditCtrl {
         return;
       }
 
-      this.testing = {done: false};
+      this.testing = {done: false, status: 'error'};
 
       // make test call in no backend cache context
       this.backendSrv.withNoBackendCache(() => {
         return datasource.testDatasource().then(result => {
           this.testing.message = result.message;
           this.testing.status = result.status;
-          this.testing.title = result.title;
         }).catch(err => {
           if (err.statusText) {
-            this.testing.message = err.statusText;
-            this.testing.title = "HTTP Error";
+            this.testing.message = 'HTTP Error ' + err.statusText;
           } else {
             this.testing.message = err.message;
-            this.testing.title = "Unknown error";
           }
         });
       }).finally(() => {

+ 8 - 3
public/app/features/plugins/partials/ds_edit.html

@@ -59,9 +59,14 @@
 
 			<div ng-if="ctrl.testing" class="gf-form-group">
 				<h5 ng-show="!ctrl.testing.done">Testing.... <i class="fa fa-spiner fa-spin"></i></h5>
-				<div class="alert-{{ctrl.testing.status}} alert">
-					<div class="alert-title">{{ctrl.testing.title}}</div>
-					<div ng-bind='ctrl.testing.message'></div>
+				<div class="alert-{{ctrl.testing.status}} alert" ng-show="ctrl.testing.done">
+					<div class="alert-icon">
+							<i class="fa fa-exclamation-triangle" ng-show="ctrl.testing.status === 'error'"></i>
+							<i class="fa fa-check" ng-show="ctrl.testing.status !== 'error'"></i>
+					</div>
+					<div class="alert-body">
+						<div class="alert-title">{{ctrl.testing.message}}</div>
+					</div>
 				</div>
 			</div>
 

+ 1 - 1
public/app/plugins/datasource/cloudwatch/datasource.js

@@ -335,7 +335,7 @@ function (angular, _, moment, dateMath, kbn, templatingVariable, CloudWatchAnnot
       var dimensions = {};
 
       return this.getDimensionValues(region, namespace, metricName, 'ServiceName', dimensions).then(function () {
-        return { status: 'success', message: 'Data source is working', title: 'Success' };
+        return { status: 'success', message: 'Data source is working' };
       });
     };
 

+ 4 - 4
public/app/plugins/datasource/elasticsearch/datasource.js

@@ -175,9 +175,9 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
       return this.getFields({type: 'date'}).then(function(dateFields) {
         var timeField = _.find(dateFields, {text: this.timeField});
         if (!timeField) {
-          return { status: "error", message: "No date field named " + this.timeField + ' found', title: "Error" };
+          return { status: "error", message: "No date field named " + this.timeField + ' found' };
         }
-        return { status: "success", message: "Index OK. Time field name OK.", title: "Success" };
+        return { status: "success", message: "Index OK. Time field name OK." };
       }.bind(this), function(err) {
         console.log(err);
         if (err.data && err.data.error) {
@@ -185,9 +185,9 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
           if (err.data.error.reason) {
             message = err.data.error.reason;
           }
-          return { status: "error", message: message, title: "Error" };
+          return { status: "error", message: message };
         } else {
-          return { status: "error", message: err.status, title: "Error" };
+          return { status: "error", message: err.status };
         }
       });
     };

+ 1 - 1
public/app/plugins/datasource/graphite/datasource.ts

@@ -205,7 +205,7 @@ export function GraphiteDatasource(instanceSettings, $q, backendSrv, templateSrv
 
   this.testDatasource = function() {
     return this.metricFindQuery('*').then(function () {
-      return { status: "success", message: "Data source is working", title: "Success" };
+      return { status: "success", message: "Data source is working"};
     });
   };
 

+ 5 - 0
public/app/plugins/datasource/graphite/specs/query_ctrl_specs.ts

@@ -24,6 +24,11 @@ describe('GraphiteQueryCtrl', function() {
     ctx.target = {target: 'aliasByNode(scaleToSeconds(test.prod.*,1),2)'};
     ctx.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([]));
     ctx.panelCtrl = {panel: {}};
+    ctx.panelCtrl = {
+      panel: {
+        targets: [ctx.target]
+      }
+    };
     ctx.panelCtrl.refresh = sinon.spy();
 
     ctx.ctrl = $controller(GraphiteQueryCtrl, {$scope: ctx.scope}, {

+ 3 - 3
public/app/plugins/datasource/influxdb/datasource.ts

@@ -196,11 +196,11 @@ export default class InfluxDatasource {
     return this.metricFindQuery('SHOW DATABASES').then(res => {
       let found = _.find(res, {text: this.database});
       if (!found) {
-        return { status: "error", message: "Could not find the specified database name.", title: "DB Not found" };
+        return { status: "error", message: "Could not find the specified database name." };
       }
-      return { status: "success", message: "Data source is working", title: "Success" };
+      return { status: "success", message: "Data source is working" };
     }).catch(err => {
-      return { status: "error", message: err.message, title: "Test Failed" };
+      return { status: "error", message: err.message };
     });
   }
 

+ 1 - 1
public/app/plugins/datasource/influxdb/partials/query.editor.html

@@ -92,7 +92,7 @@
     <div class="gf-form-inline" ng-if="ctrl.target.orderByTime === 'DESC'">
       <div class="gf-form">
         <label class="gf-form-label query-keyword width-7">ORDER BY</label>
-        <label class="gf-form-label pointer" ng-click="ctrl.removeOrderByTime()">time <span classs="query-keyword">DESC</span> <i class="fa fa-remove"></i></label>
+        <label class="gf-form-label pointer" ng-click="ctrl.removeOrderByTime()">time <span class="query-keyword">DESC</span> <i class="fa fa-remove"></i></label>
       </div>
       <div class="gf-form gf-form--grow">
 				<div class="gf-form-label gf-form-label--grow"></div>

+ 6 - 2
public/app/plugins/datasource/influxdb/specs/query_ctrl_specs.ts

@@ -19,9 +19,13 @@ describe('InfluxDBQueryCtrl', function() {
     ctx.$q = $q;
     ctx.scope = $rootScope.$new();
     ctx.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([]));
-    ctx.panelCtrl = {panel: {}};
-    ctx.panelCtrl.refresh = sinon.spy();
     ctx.target = {target: {}};
+    ctx.panelCtrl = {
+      panel: {
+        targets: [ctx.target]
+      }
+    };
+    ctx.panelCtrl.refresh = sinon.spy();
     ctx.ctrl = $controller(InfluxQueryCtrl, {$scope: ctx.scope}, {
       panelCtrl: ctx.panelCtrl,
       target: ctx.target,

+ 3 - 3
public/app/plugins/datasource/mysql/datasource.ts

@@ -118,13 +118,13 @@ export class MysqlDatasource {
         }],
       }
     }).then(res => {
-      return { status: "success", message: "Database Connection OK", title: "Success" };
+      return { status: "success", message: "Database Connection OK"};
     }).catch(err => {
       console.log(err);
       if (err.data && err.data.message) {
-        return { status: "error", message: err.data.message, title: "Error" };
+        return { status: "error", message: err.data.message };
       } else {
-        return { status: "error", message: err.status, title: "Error" };
+        return { status: "error", message: err.status };
       }
     });
   }

+ 1 - 1
public/app/plugins/datasource/opentsdb/datasource.js

@@ -296,7 +296,7 @@ function (angular, _, dateMath) {
 
     this.testDatasource = function() {
       return this._performSuggestQuery('cpu', 'metrics').then(function () {
-        return { status: "success", message: "Data source is working", title: "Success" };
+        return { status: "success", message: "Data source is working" };
       });
     };
 

+ 5 - 1
public/app/plugins/datasource/opentsdb/specs/query-ctrl-specs.ts

@@ -18,7 +18,11 @@ describe('OpenTsQueryCtrl', function() {
     ctx.$q = $q;
     ctx.scope = $rootScope.$new();
     ctx.target = {target: ''};
-    ctx.panelCtrl = {panel: {}};
+    ctx.panelCtrl = {
+      panel: {
+        targets: [ctx.target]
+      }
+    };
     ctx.panelCtrl.refresh = sinon.spy();
     ctx.datasource.getAggregators = sinon.stub().returns(ctx.$q.when([]));
     ctx.datasource.getFilterTypes = sinon.stub().returns(ctx.$q.when([]));

+ 1 - 1
public/app/plugins/datasource/prometheus/datasource.ts

@@ -241,7 +241,7 @@ export class PrometheusDatasource {
 
   testDatasource() {
     return this.metricFindQuery('metrics(.*)').then(function() {
-      return { status: 'success', message: 'Data source is working', title: 'Success' };
+      return { status: 'success', message: 'Data source is working'};
     });
   }
 

+ 1 - 1
public/app/plugins/datasource/prometheus/partials/query.editor.html

@@ -2,7 +2,7 @@
 	<div class="gf-form-inline">
 		<div class="gf-form gf-form--grow">
 			<code-editor content="ctrl.target.expr" on-change="ctrl.refreshMetricData()"
-				get-completer="ctrl.getCompleter()" data-mode="prometheus">
+				get-completer="ctrl.getCompleter()" data-mode="prometheus" code-editor-focus="ctrl.isLastQuery">
 			</code-editor>
 		</div>
 	</div>

+ 27 - 0
public/app/plugins/datasource/prometheus/specs/completer_specs.ts

@@ -0,0 +1,27 @@
+import {describe, beforeEach, it, sinon, expect} from 'test/lib/common';
+
+import {PromCompleter} from '../completer';
+import {PrometheusDatasource} from '../datasource';
+
+describe('Prometheus editor completer', function() {
+
+  let editor = {};
+  let session = {
+    getTokenAt: sinon.stub().returns({}),
+    getLine:  sinon.stub().returns(""),
+  };
+
+  let datasourceStub = <PrometheusDatasource>{};
+  let completer = new PromCompleter(datasourceStub);
+
+  describe("When inside brackets", () => {
+
+    it("Should return range vectors", () => {
+      completer.getCompletions(editor, session, 10, "[", (s, res) => {
+        expect(res[0]).to.eql({caption: '1s', value: '[1s', meta: 'range vector'});
+      });
+    });
+
+  });
+
+});

+ 1 - 1
public/app/plugins/panel/text/editor.html

@@ -3,7 +3,7 @@
 		<div class="gf-form">
 			<span class="gf-form-label">Mode</span>
 			<span class="gf-form-select-wrapper">
-				<select class="gf-form-input" ng-model="ctrl.panel.mode" ng-options="f for f in ['html','markdown','text']"></select>
+				<select class="gf-form-input" ng-model="ctrl.panel.mode" ng-options="f for f in ['html','markdown']"></select>
 			</span>
 		</div>
 	</div>

+ 12 - 12
public/sass/_variables.dark.scss

@@ -103,7 +103,7 @@ $tight-form-func-bg: 		    #333;
 $tight-form-func-highlight-bg:  #444;
 
 $modal-background: $black;
-$code-tag-bg:      $dark-5;
+$code-tag-bg:      $gray-1;
 $code-tag-border:  lighten($code-tag-bg, 2%);
 
 
@@ -238,17 +238,15 @@ $paginationActiveBackground:          $blue;
 
 // Form states and alerts
 // -------------------------
-$state-warning-text:      $warn;
-$state-warning-bg:        $brand-warning;
+$warning-text-color:      $warn;
+$error-text-color:        #E84D4D;
+$success-text-color:      #12D95A;
+$info-text-color:         $blue-dark;
 
-$errorText:               #E84D4D;
-$errorBackground:         $btn-danger-bg;
-
-$successText:             #12D95A;
-$successBackground:       $btn-success-bg;
-
-$infoText:                $blue-dark;
-$infoBackground:          $blue-dark;
+$alert-error-bg:          linear-gradient(90deg, #d44939, #e0603d);
+$alert-success-bg:        linear-gradient(90deg, #3aa655, #47b274);
+$alert-warning-bg:        linear-gradient(90deg, #d44939, #e0603d);
+$alert-info-bg:           linear-gradient(100deg, #1a4552, #00374a);
 
 // popover
 $popover-bg:              $panel-bg;
@@ -258,6 +256,8 @@ $popover-border-color:    $gray-1;
 $popover-help-bg:         $btn-secondary-bg;
 $popover-help-color:      $text-color;
 
+$popover-error-bg:        $btn-danger-bg;
+
 // Tooltips and popovers
 // -------------------------
 $tooltipColor:            $popover-help-color;
@@ -276,7 +276,7 @@ $card-background-hover: linear-gradient(135deg, #343434, #262626);
 $card-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, .1), 1px 1px 0 0 rgba(0, 0, 0, .3);
 
 // info box
-$info-box-background: linear-gradient(100deg, #1a4552, #0b2127);
+$info-box-background: linear-gradient(100deg, #1a4552, #00374a);
 
 // footer
 $footer-link-color:   $gray-1;

+ 9 - 15
public/sass/_variables.light.scss

@@ -259,29 +259,23 @@ $paginationActiveBackground:          $blue;
 
 // Form states and alerts
 // -------------------------
-$state-warning-text:             lighten($orange, 10%);
-$state-warning-bg:       $orange;
-$warningBorder:           transparent;
+$warning-text-color:            lighten($orange, 10%);
+$error-text-color:              lighten($red, 10%);
+$success-text-color:            lighten($green, 10%);
+$info-text-color:                     $blue;
 
-$errorText:               lighten($red, 10%);
-$errorBackground:         $red;
-$errorBorder:             transparent;
-
-$successText:             lighten($green, 10%);
-$successBackground:       $green;
-$successBorder:           transparent;
-
-$infoText:                $blue;
-$infoBackground:          $blue-dark;
-$infoBorder:              transparent;
+$alert-error-bg:          linear-gradient(90deg, #d44939, #e0603d);
+$alert-success-bg:        linear-gradient(90deg, #3aa655, #47b274);
+$alert-warning-bg:        linear-gradient(90deg, #d44939, #e0603d);
+$alert-info-bg:           $blue-dark;
 
 // popover
 $popover-bg:              $gray-5;
 $popover-color:           $text-color;
 $popover-border-color:    $gray-3;
-
 $popover-help-bg:         $blue-dark;
 $popover-help-color:      $gray-6;
+$popover-error-bg:        $btn-danger-bg;
 
 // Tooltips and popovers
 // -------------------------

+ 9 - 9
public/sass/base/_type.scss

@@ -31,21 +31,21 @@ cite    { font-style: normal; }
 a.muted:hover,
 a.muted:focus        { color: darken($text-muted, 10%); }
 
-.text-warning        { color: $state-warning-text; }
+.text-warning        { color: $warning-text-color; }
 a.text-warning:hover,
-a.text-warning:focus { color: darken($state-warning-text, 10%); }
+a.text-warning:focus { color: darken($warning-text-color, 10%); }
 
-.text-error          { color: $errorText; }
+.text-error          { color: $error-text-color; }
 a.text-error:hover,
-a.text-error:focus   { color: darken($errorText, 10%); }
+a.text-error:focus   { color: darken($error-text-color, 10%); }
 
-.text-info           { color: $infoText; }
+.text-info           { color: $info-text-color; }
 a.text-info:hover,
-a.text-info:focus    { color: darken($infoText, 10%); }
+a.text-info:focus    { color: darken($info-text-color, 10%); }
 
-.text-success        { color: $successText; }
+.text-success        { color: $success-text-color; }
 a.text-success:hover,
-a.text-success:focus { color: darken($successText, 10%); }
+a.text-success:focus { color: darken($success-text-color, 10%); }
 a { cursor: pointer; }
 
 a[disabled] {
@@ -130,7 +130,7 @@ small,
 mark,
 .mark {
   padding: .2em;
-  background-color: $state-warning-bg;
+  background: $alert-warning-bg;
 }
 
 

+ 34 - 22
public/sass/components/_alerts.scss

@@ -7,60 +7,57 @@
 // -------------------------
 
 .alert {
-  padding: 0.5rem 2rem 0.5rem 1rem;
+  padding: 1.25rem 2rem 1.25rem 1.5rem;
   margin-bottom: $line-height-base;
-  text-shadow: 0 1px 0 rgba(255,255,255,.5);
-  background-color: $state-warning-bg;
+  text-shadow: 0 2px 0 rgba(255,255,255,.5);
+  background: $alert-error-bg;
   position: relative;
   color: $white;
-  text-shadow: 0 1px 0 rgba(0,0,0,.5);
+  text-shadow: 0 1px 0 rgba(0,0,0,.2);
   border-radius: 2px;
+  display: flex;
+  flex-direction: row;
 }
 
 // Alternate styles
 // -------------------------
 
 .alert-success {
-  background-color: $successBackground;
+  background: $alert-success-bg;
 }
 
 .alert-danger,
 .alert-error {
-  background-color: $errorBackground;
+  background: $alert-error-bg;
 }
 
 .alert-info {
-  background-color: $infoBackground;
+  background: $alert-info-bg;
 }
 
 .alert-warning {
-  background-color: $state-warning-bg;
+  background: $alert-warning-bg;
 }
 
 .page-alert-list {
   z-index: 8000;
-  min-width: 300px;
-  max-width: 300px;
+  min-width: 400px;
+  max-width: 600px;
   position: fixed;
   right: 20px;
   top: 56px;
 }
 
 .alert-close {
-  position: absolute;
-  top: -4px;
-  right: -2px;
-  width: 16px;
-  height: 16px;
-  padding: 0;
-  background: $white;
-  border-radius: 50%;
+  padding: 0 0 0 1rem;
   border: none;
-  font-size: 1.1rem;
-  color: $dark-4;
+  background: none;
+  display: flex;
+  align-items: center;
   .fa {
-    position: relative;
-    top: -2px;
+    align-self: flex-end;
+    font-size: 1.5rem;
+    color: rgba(255,255,255,.75)
   }
 }
 
@@ -68,3 +65,18 @@
   font-weight: $font-weight-semi-bold;
   padding-bottom: 2px;
 }
+
+.alert-icon {
+  padding: 0 1rem 0 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 2.5rem;
+  .fa {
+    font-size: 1.5rem;
+  }
+}
+
+.alert-body {
+  flex-grow: 1;
+}

+ 5 - 1
public/sass/components/_code_editor.scss

@@ -28,7 +28,7 @@
   background-color: $dropdownBackground !important;
   color: $dropdownLinkColor !important;
   border: 1px solid $dropdownBorder !important;
-  width: 320px !important;
+  width: 550px !important;
 
   .ace_scroller {
     .ace_selected, .ace_active-line, .ace_line-hover {
@@ -77,3 +77,7 @@ $doc-font-size: $font-size-sm;
 .ace_tooltip {
   border-radius: 3px;
 }
+
+.ace_hidden-cursors .ace_cursor {
+  opacity: 0 !important;
+}

+ 1 - 1
public/sass/components/_drop.scss

@@ -5,7 +5,7 @@ $useDropShadow: false;
 $attachmentOffset: 0%;
 $easing: cubic-bezier(0, 0, 0.265, 1.00);
 
-@include drop-theme("error", $errorBackground, $popover-color);
+@include drop-theme("error", $popover-error-bg, $popover-color);
 @include drop-theme("popover", $popover-bg, $popover-color, $popover-border-color);
 @include drop-theme("help", $popover-help-bg, $popover-help-color);
 

+ 1 - 1
public/sass/pages/_dashboard.scss

@@ -138,7 +138,7 @@ div.flot-text {
   &--error {
     display: block;
     color: $text-color;
-    @include panel-corner-color($errorBackground);
+    @include panel-corner-color($popover-error-bg);
     .fa:before {
       content: "\f12a";
     }

+ 1 - 1
public/test/core/utils/kbn_specs.js

@@ -176,7 +176,7 @@ define([
     });
 
     it('86399s 1 resolution', function() {
-      var range = { from: dateMath.parse('now-86399s'), to: dateMath.parse('now') };
+      var range = { from: dateMath.parse('now-86390s'), to: dateMath.parse('now') };
       var res = kbn.calculateInterval(range, 1, null);
       expect(res.interval).to.be('12h');
       expect(res.intervalMs).to.be(43200000);

+ 6 - 3
public/views/index.html

@@ -31,11 +31,14 @@
 
 			<div class="page-alert-list">
 				<div ng-repeat='alert in dashAlerts.list' class="alert-{{alert.severity}} alert">
+					<div class="alert-icon"><i class="{{alert.icon}}"></i></div>
+					<div class="alert-body">
+						<div class="alert-title">{{alert.title}}</div>
+						<div class="alert-text" ng-bind='alert.text'></div>
+					</div>
 					<button type="button" class="alert-close" ng-click="dashAlerts.clear(alert)">
-						<i class="fa fa-times-circle"></i>
+						<i class="fa fa fa-remove"></i>
 					</button>
-					<div class="alert-title">{{alert.title}}</div>
-					<div ng-bind='alert.text'></div>
 				</div>
 			</div>