Explorar o código

Merge branch 'master' into develop

Torkel Ödegaard %!s(int64=8) %!d(string=hai) anos
pai
achega
d8230f8fa7
Modificáronse 28 ficheiros con 124 adicións e 74 borrados
  1. 14 2
      CHANGELOG.md
  2. 1 1
      PLUGIN_DEV.md
  3. 1 1
      docs/sources/features/datasources/cloudwatch.md
  4. 1 1
      package.json
  5. 4 4
      packaging/publish/publish_testing.sh
  6. 10 0
      pkg/services/alerting/conditions/reducer.go
  7. 29 0
      pkg/services/alerting/conditions/reducer_test.go
  8. 1 1
      pkg/services/sqlstore/annotation.go
  9. 1 1
      pkg/tsdb/cloudwatch/metric_find_query.go
  10. 2 4
      public/app/core/components/PasswordStrength.tsx
  11. 7 8
      public/app/core/components/colorpicker/ColorPalette.tsx
  12. 11 16
      public/app/core/components/colorpicker/ColorPicker.tsx
  13. 0 5
      public/app/core/components/colorpicker/ColorPickerPopover.tsx
  14. 3 5
      public/app/core/components/colorpicker/SeriesColorPicker.tsx
  15. 0 4
      public/app/core/components/colorpicker/SpectrumPicker.tsx
  16. 1 0
      public/app/core/components/colorpicker/spectrum_picker.ts
  17. 2 3
      public/app/core/utils/file_export.ts
  18. 10 0
      public/app/core/utils/react2angular.ts
  19. 1 0
      public/app/features/alerting/alert_def.ts
  20. 1 0
      public/app/features/alerting/alert_tab_ctrl.ts
  21. 1 1
      public/app/features/alerting/partials/alert_tab.html
  22. 14 2
      public/app/features/plugins/plugin_loader.ts
  23. 1 1
      public/app/partials/login.html
  24. 3 1
      public/app/plugins/datasource/cloudwatch/datasource.js
  25. 2 2
      public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts
  26. 2 2
      public/sass/_variables.light.scss
  27. 1 0
      public/sass/components/_gf-form.scss
  28. 0 9
      public/sass/components/_panel_graph.scss

+ 14 - 2
CHANGELOG.md

@@ -7,9 +7,20 @@
 - UX changes to nav & side menu
 - New dashboard grid layout system
 
-# 4.6.0 (unreleased)
+# 4.6.0-beta2 (2017-10-17)
+
+## Fixes
+* **ColorPicker**: Fix for color picker not showing [#9549](https://github.com/grafana/grafana/issues/9549)
+* **Alerting**: Fix for broken test rule button in alert tab [#9539](https://github.com/grafana/grafana/issues/9539)
+* **Cloudwatch**: Provide error message when failing to add cloudwatch datasource [#9534](https://github.com/grafana/grafana/pull/9534), thx [@mtanda](https://github.com/mtanda)
+* **Cloudwatch**: Fix unused period parameter [#9536](https://github.com/grafana/grafana/pull/9536), thx [@mtanda](https://github.com/mtanda)
+* **CSV Export**: Fix for broken CSV export [#9525](https://github.com/grafana/grafana/issues/9525)
+* **Text panel**: Fixes issue with break lines in Firefox [#9491](https://github.com/grafana/grafana/issues/9491)
+
+# 4.6.0-beta1 (2017-10-13)
 
 ## New Features
+* **Annotations**: Add support for creating annotations from graph panel [#8197](https://github.com/grafana/grafana/pull/8197)
 * **GCS**: Adds support for Google Cloud Storage [#8370](https://github.com/grafana/grafana/issues/8370) thx [@chuhlomin](https://github.com/chuhlomin)
 * **Prometheus**: Adds /metrics endpoint for exposing Grafana metrics. [#9187](https://github.com/grafana/grafana/pull/9187)
 * **Graph**: Add support for local formating in axis. [#1395](https://github.com/grafana/grafana/issues/1395), thx [@m0nhawk](https://github.com/m0nhawk)
@@ -36,10 +47,11 @@
 * **Table**: Add support for displaying the timestamp with milliseconds [#9429](https://github.com/grafana/grafana/pull/9429), thx [@s1061123](https://github.com/s1061123)
 * **Hipchat**: Add metrics, message and image to hipchat notifications [#9110](https://github.com/grafana/grafana/issues/9110), thx [@eloo](https://github.com/eloo)
 * **Kafka**: Add support for sending alert notifications to kafka [#7104](https://github.com/grafana/grafana/issues/7104), thx [@utkarshcmu](https://github.com/utkarshcmu)
+* **Alerting**: add count_non_null as series reducer [#9516](https://github.com/grafana/grafana/issues/9516)
 
 ## Tech
 * **Go**: Grafana is now built using golang 1.9
-* **Webpack**: Changed from systemjs to webpack (see readme or building from source guide for new build instructions). Systemjs is still used to load plugins but now plugins can only import a limited set of dependencies. See [PLUGIN_DEV.md](https://github.com/grafana/grafana/blob/master/PLUGIN_DEV.md) for more details on how this can effect some plugins. 
+* **Webpack**: Changed from systemjs to webpack (see readme or building from source guide for new build instructions). Systemjs is still used to load plugins but now plugins can only import a limited set of dependencies. See [PLUGIN_DEV.md](https://github.com/grafana/grafana/blob/master/PLUGIN_DEV.md) for more details on how this can effect some plugins.
 
 # 4.5.2 (2017-09-22)
 

+ 1 - 1
PLUGIN_DEV.md

@@ -23,6 +23,6 @@ If you think we missed exposing a crucial lib or Grafana component let us know b
 
 ### Deprecated components 
 
-The angular directive `<spectrum-picker>` is no deprecated (will still work for a version more) but we recommend plugin authors
+The angular directive `<spectrum-picker>` is now deprecated (will still work for a version more) but we recommend plugin authors
 to upgrade to new `<color-picker color="ctrl.color" onChange="ctrl.onSparklineColorChange"></color-picker>`
 

+ 1 - 1
docs/sources/features/datasources/cloudwatch.md

@@ -29,7 +29,7 @@ Name | Description
 ------------ | -------------
 *Name* | The data source name. This is how you refer to the data source in panels & queries.
 *Default* | Default data source means that it will be pre-selected for new panels.
-*Credentials* profile name | Specify the name of the profile to use (if you use `~/aws/credentials` file), leave blank for default.
+*Credentials* profile name | Specify the name of the profile to use (if you use `~/.aws/credentials` file), leave blank for default.
 *Default Region* | Used in query editor to set region (can be changed on per query basis)
 *Custom Metrics namespace* | Specify the CloudWatch namespace of Custom metrics
 *Assume Role Arn* | Specify the ARN of the role to assume

+ 1 - 1
package.json

@@ -4,7 +4,7 @@
     "company": "Grafana Labs"
   },
   "name": "grafana",
-  "version": "4.6.0-beta1",
+  "version": "4.7.0-pre1",
   "repository": {
     "type": "git",
     "url": "http://github.com/grafana/grafana.git"

+ 4 - 4
packaging/publish/publish_testing.sh

@@ -1,10 +1,10 @@
 #! /usr/bin/env bash
-deb_ver=4.5.0-beta1
-rpm_ver=4.5.0-beta1
+deb_ver=4.6.0-beta1
+rpm_ver=4.6.0-beta1
 
-# wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_${deb_ver}_amd64.deb
+wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_${deb_ver}_amd64.deb
 
-# package_cloud push grafana/testing/debian/jessie grafana_${deb_ver}_amd64.deb
+package_cloud push grafana/testing/debian/jessie grafana_${deb_ver}_amd64.deb
 package_cloud push grafana/testing/debian/wheezy grafana_${deb_ver}_amd64.deb
 package_cloud push grafana/testing/debian/stretch grafana_${deb_ver}_amd64.deb
 

+ 10 - 0
pkg/services/alerting/conditions/reducer.go

@@ -141,6 +141,16 @@ func (s *SimpleReducer) Reduce(series *tsdb.TimeSeries) null.Float {
 				break
 			}
 		}
+	case "count_non_null":
+		for _, v := range series.Points {
+			if v[0].Valid {
+				value++
+			}
+		}
+
+		if value > 0 {
+			allNull = false
+		}
 	}
 
 	if allNull {

+ 29 - 0
pkg/services/alerting/conditions/reducer_test.go

@@ -67,6 +67,35 @@ func TestSimpleReducer(t *testing.T) {
 			So(reducer.Reduce(series).Valid, ShouldEqual, false)
 		})
 
+		Convey("count_non_null", func() {
+			Convey("with null values and real values", func() {
+				reducer := NewSimpleReducer("count_non_null")
+				series := &tsdb.TimeSeries{
+					Name: "test time serie",
+				}
+
+				series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 1))
+				series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 2))
+				series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(3), 3))
+				series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(3), 4))
+
+				So(reducer.Reduce(series).Valid, ShouldEqual, true)
+				So(reducer.Reduce(series).Float64, ShouldEqual, 2)
+			})
+
+			Convey("with null values", func() {
+				reducer := NewSimpleReducer("count_non_null")
+				series := &tsdb.TimeSeries{
+					Name: "test time serie",
+				}
+
+				series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 1))
+				series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 2))
+
+				So(reducer.Reduce(series).Valid, ShouldEqual, false)
+			})
+		})
+
 		Convey("avg of number values and null values should ignore nulls", func() {
 			reducer := NewSimpleReducer("avg")
 			series := &tsdb.TimeSeries{

+ 1 - 1
pkg/services/sqlstore/annotation.go

@@ -43,7 +43,7 @@ func (r *SqlAnnotationRepo) ensureTagsExist(sess *DBSession, tags []*models.Tag)
 		var existingTag models.Tag
 
 		// check if it exists
-		if exists, err := sess.Table("tag").Where("key=? AND value=?", tag.Key, tag.Value).Get(&existingTag); err != nil {
+		if exists, err := sess.Table("tag").Where("`key`=? AND `value`=?", tag.Key, tag.Value).Get(&existingTag); err != nil {
 			return nil, err
 		} else if exists {
 			tag.Id = existingTag.Id

+ 1 - 1
pkg/tsdb/cloudwatch/metric_find_query.go

@@ -38,7 +38,7 @@ var customMetricsDimensionsMap map[string]map[string]map[string]*CustomMetricsCa
 func init() {
 	metricsMap = map[string][]string{
 		"AWS/ApiGateway":     {"4XXError", "5XXError", "CacheHitCount", "CacheMissCount", "Count", "IntegrationLatency", "Latency"},
-		"AWS/ApplicationELB": {"ActiveConnectionCount", "ClientTLSNegotiationErrorCount", "HealthyHostCount", "HTTPCode_ELB_4XX_Count", "HTTPCode_ELB_5XX_Count", "HTTPCode_Target_2XX_Count", "HTTPCode_Target_3XX_Count", "HTTPCode_Target_4XX_Count", "HTTPCode_Target_5XX_Count", "IPv6ProcessedBytes", "IPv6RequestCount", "NewConnectionCount", "ProcessedBytes", "RejectedConnectionCount", "RequestCount", "TargetConnectionErrorCount", "TargetResponseTime", "TargetTLSNegotiationErrorCount", "UnHealthyHostCount"},
+		"AWS/ApplicationELB": {"ActiveConnectionCount", "ClientTLSNegotiationErrorCount", "HealthyHostCount", "HTTPCode_ELB_4XX_Count", "HTTPCode_ELB_5XX_Count", "HTTPCode_Target_2XX_Count", "HTTPCode_Target_3XX_Count", "HTTPCode_Target_4XX_Count", "HTTPCode_Target_5XX_Count", "IPv6ProcessedBytes", "IPv6RequestCount", "NewConnectionCount", "ProcessedBytes", "RejectedConnectionCount", "RequestCount", "RequestCountPerTarget", "TargetConnectionErrorCount", "TargetResponseTime", "TargetTLSNegotiationErrorCount", "UnHealthyHostCount"},
 		"AWS/AutoScaling":    {"GroupMinSize", "GroupMaxSize", "GroupDesiredCapacity", "GroupInServiceInstances", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"},
 		"AWS/Billing":        {"EstimatedCharges"},
 		"AWS/CloudFront":     {"Requests", "BytesDownloaded", "BytesUploaded", "TotalErrorRate", "4xxErrorRate", "5xxErrorRate"},

+ 2 - 4
public/app/core/components/PasswordStrength.tsx

@@ -1,5 +1,5 @@
 import React from 'react';
-import coreModule from '../core_module';
+import { react2AngularDirective } from 'app/core/utils/react2angular';
 
 export interface IProps {
   password: string;
@@ -33,7 +33,5 @@ export class PasswordStrength extends React.Component<IProps, any> {
   }
 }
 
-coreModule.directive('passwordStrength', function(reactDirective) {
-  return reactDirective(PasswordStrength, ['password']);
-});
+react2AngularDirective('passwordStrength', PasswordStrength, ['password']);
 

+ 7 - 8
public/app/core/components/colorpicker/ColorPalette.tsx

@@ -1,5 +1,4 @@
 import React from 'react';
-import coreModule from 'app/core/core_module';
 import { sortedColors } from 'app/core/utils/colors';
 
 export interface IProps {
@@ -23,12 +22,15 @@ export class GfColorPalette extends React.Component<IProps, any> {
   }
 
   render() {
-    const colorPaletteItems = this.paletteColors.map((paletteColor) => {
+    const colorPaletteItems = this.paletteColors.map(paletteColor => {
       const cssClass = paletteColor.toLowerCase() === this.props.color.toLowerCase() ? 'fa-circle-o' : 'fa-circle';
       return (
-        <i key={paletteColor} className={"pointer fa " + cssClass}
-          style={{'color': paletteColor}}
-          onClick={this.onColorSelect(paletteColor)}>&nbsp;
+        <i
+          key={paletteColor}
+          className={'pointer fa ' + cssClass}
+          style={{ color: paletteColor }}
+          onClick={this.onColorSelect(paletteColor)}>
+          &nbsp;
         </i>
       );
     });
@@ -40,6 +42,3 @@ export class GfColorPalette extends React.Component<IProps, any> {
   }
 }
 
-coreModule.directive('gfColorPalette', function (reactDirective) {
-  return reactDirective(GfColorPalette, ['color', 'onColorSelect']);
-});

+ 11 - 16
public/app/core/components/colorpicker/ColorPicker.tsx

@@ -2,8 +2,8 @@ import React from 'react';
 import ReactDOM from 'react-dom';
 import $ from 'jquery';
 import Drop from 'tether-drop';
-import coreModule from 'app/core/core_module';
 import { ColorPickerPopover } from './ColorPickerPopover';
+import { react2AngularDirective } from 'app/core/utils/react2angular';
 
 export interface IProps {
   color: string;
@@ -27,9 +27,7 @@ export class ColorPicker extends React.Component<IProps, any> {
   }
 
   openColorPicker() {
-    const dropContent = (
-      <ColorPickerPopover color={this.props.color} onColorSelect={this.onColorSelect} />
-    );
+    const dropContent = <ColorPickerPopover color={this.props.color} onColorSelect={this.onColorSelect} />;
 
     let dropContentElem = document.createElement('div');
     ReactDOM.render(dropContent, dropContentElem);
@@ -38,12 +36,12 @@ export class ColorPicker extends React.Component<IProps, any> {
       target: this.pickerElem[0],
       content: dropContentElem,
       position: 'top center',
-      classes: 'drop-popover drop-popover--form',
-      openOn: 'hover',
+      classes: 'drop-popover',
+      openOn: 'click',
       hoverCloseDelay: 200,
       tetherOptions: {
-        constraints: [{ to: 'scrollParent', attachment: "none both" }]
-      }
+        constraints: [{ to: 'scrollParent', attachment: 'none both' }],
+      },
     });
 
     drop.on('close', this.closeColorPicker);
@@ -68,17 +66,14 @@ export class ColorPicker extends React.Component<IProps, any> {
     return (
       <div className="sp-replacer sp-light" onClick={this.openColorPicker} ref={this.setPickerElem}>
         <div className="sp-preview">
-          <div className="sp-preview-inner" style={{backgroundColor: this.props.color}}>
-          </div>
+          <div className="sp-preview-inner" style={{ backgroundColor: this.props.color }} />
         </div>
       </div>
     );
   }
 }
 
-coreModule.directive('colorPicker', function (reactDirective) {
-  return reactDirective(ColorPicker, [
-    'color',
-    ['onChange', { watchDepth: 'reference', wrapApply: true }]
-  ]);
-});
+react2AngularDirective('colorPicker', ColorPicker, [
+  'color',
+  ['onChange', { watchDepth: 'reference', wrapApply: true }],
+]);

+ 0 - 5
public/app/core/components/colorpicker/ColorPickerPopover.tsx

@@ -1,7 +1,6 @@
 import React from 'react';
 import $ from 'jquery';
 import tinycolor from 'tinycolor2';
-import coreModule from 'app/core/core_module';
 import { GfColorPalette } from './ColorPalette';
 import { GfSpectrumPicker } from './SpectrumPicker';
 
@@ -115,7 +114,3 @@ export class ColorPickerPopover extends React.Component<IProps, any> {
     );
   }
 }
-
-coreModule.directive('gfColorPickerPopover', function (reactDirective) {
-  return reactDirective(ColorPickerPopover, ['color', 'onColorSelect']);
-});

+ 3 - 5
public/app/core/components/colorpicker/SeriesColorPicker.tsx

@@ -1,6 +1,6 @@
 import React from 'react';
-import coreModule from 'app/core/core_module';
-import {ColorPickerPopover} from './ColorPickerPopover';
+import { ColorPickerPopover } from './ColorPickerPopover';
+import { react2AngularDirective } from 'app/core/utils/react2angular';
 
 export interface IProps {
   series: any;
@@ -50,6 +50,4 @@ export class SeriesColorPicker extends React.Component<IProps, any> {
   }
 }
 
-coreModule.directive('seriesColorPicker', function(reactDirective) {
-  return reactDirective(SeriesColorPicker, ['series', 'onColorChange', 'onToggleAxis']);
-});
+react2AngularDirective('seriesColorPicker', SeriesColorPicker, ['series', 'onColorChange', 'onToggleAxis']);

+ 0 - 4
public/app/core/components/colorpicker/SpectrumPicker.tsx

@@ -1,5 +1,4 @@
 import React from 'react';
-import coreModule from 'app/core/core_module';
 import _ from 'lodash';
 import $ from 'jquery';
 import 'vendor/spectrum';
@@ -71,6 +70,3 @@ export class GfSpectrumPicker extends React.Component<IProps, any> {
   }
 }
 
-coreModule.directive('gfSpectrumPicker', function (reactDirective) {
-  return reactDirective(GfSpectrumPicker, ['color', 'options', 'onColorSelect']);
-});

+ 1 - 0
public/app/core/components/colorpicker/spectrum_picker.ts

@@ -5,6 +5,7 @@
  */
 import coreModule from '../../core_module';
 
+/** @ngInject */
 export function spectrumPicker() {
   return {
     restrict: 'E',

+ 2 - 3
public/app/core/utils/file_export.ts

@@ -1,7 +1,6 @@
 import _ from 'lodash';
 import moment from 'moment';
-
-declare var window: any;
+import {saveAs} from 'file-saver';
 
 const DEFAULT_DATETIME_FORMAT = 'YYYY-MM-DDTHH:mm:ssZ';
 
@@ -69,5 +68,5 @@ export function exportTableDataToCsv(table, excel = false) {
 
 export function saveSaveBlob(payload, fname) {
     var blob = new Blob([payload], { type: "text/csv;charset=utf-8" });
-    window.saveAs(blob, fname);
+    saveAs(blob, fname);
 }

+ 10 - 0
public/app/core/utils/react2angular.ts

@@ -0,0 +1,10 @@
+import coreModule from 'app/core/core_module';
+
+export function react2AngularDirective(name: string, component: any, options: any) {
+
+  coreModule.directive(name, ['reactDirective', reactDirective => {
+    return reactDirective(component, options);
+  }]);
+
+}
+

+ 1 - 0
public/app/features/alerting/alert_def.ts

@@ -51,6 +51,7 @@ var reducerTypes = [
   {text: 'median()', value: 'median'},
   {text: 'diff()', value: 'diff'},
   {text: 'percent_diff()', value: 'percent_diff'},
+  {text: 'count_non_null()', value: 'count_non_null'},
 ];
 
 var noDataModes = [

+ 1 - 0
public/app/features/alerting/alert_tab_ctrl.ts

@@ -383,6 +383,7 @@ export class AlertTabCtrl {
 
   test() {
     this.testing = true;
+    this.testResult = false;
 
     var payload = {
       dashboard: this.dashboardSrv.getCurrent().getSaveModelClone(),

+ 1 - 1
public/app/features/alerting/partials/alert_tab.html

@@ -42,7 +42,7 @@
 						<span class="gf-form-label query-keyword width-5" ng-if="$index===0">WHEN</span>
 					</div>
           			<div class="gf-form">
-						<query-part-editor class="gf-form-label query-part width-6" part="conditionModel.reducerPart" handle-event="ctrl.handleReducerPartEvent(conditionModel, $event)">
+						<query-part-editor class="gf-form-label query-part width-9" part="conditionModel.reducerPart" handle-event="ctrl.handleReducerPartEvent(conditionModel, $event)">
 						</query-part-editor>
             			<span class="gf-form-label query-keyword">OF</span>
 					</div>

+ 14 - 2
public/app/features/plugins/plugin_loader.ts

@@ -12,6 +12,9 @@ import {coreModule, appEvents, contextSrv} from 'app/core/core';
 import {Observable} from 'rxjs/Observable';
 import {Subject} from 'rxjs/Subject';
 import * as datemath from 'app/core/utils/datemath';
+import * as fileExport from 'app/core/utils/file_export';
+import * as flatten from 'app/core/utils/flatten';
+import * as ticks from 'app/core/utils/ticks';
 import builtInPlugins from './buit_in_plugins';
 import d3 from 'vendor/d3/d3';
 
@@ -54,19 +57,24 @@ exposeToPlugin('rxjs/Observable', Observable);
 exposeToPlugin('d3', d3);
 
 exposeToPlugin('app/plugins/sdk', sdk);
+
 exposeToPlugin('app/core/utils/datemath', datemath);
+exposeToPlugin('app/core/utils/file_export', fileExport);
+exposeToPlugin('app/core/utils/flatten', flatten);
 exposeToPlugin('app/core/utils/kbn', kbn);
+exposeToPlugin('app/core/utils/ticks', ticks);
+
 exposeToPlugin('app/core/config', config);
 exposeToPlugin('app/core/time_series', TimeSeries);
 exposeToPlugin('app/core/time_series2', TimeSeries);
 exposeToPlugin('app/core/table_model', TableModel);
 exposeToPlugin('app/core/app_events', appEvents);
 exposeToPlugin('app/core/core_module', coreModule);
-exposeToPlugin('app/core/core_module', coreModule);
 exposeToPlugin('app/core/core', {
   coreModule: coreModule,
   appEvents: appEvents,
   contextSrv: contextSrv,
+  __esModule: true
 });
 
 import 'vendor/flot/jquery.flot';
@@ -79,7 +87,11 @@ import 'vendor/flot/jquery.flot.fillbelow';
 import 'vendor/flot/jquery.flot.crosshair';
 import 'vendor/flot/jquery.flot.dashes';
 
-for (let flotDep of ['jquery.flot', 'jquery.flot.pie', 'jquery.flot.time']) {
+const flotDeps = [
+  'jquery.flot', 'jquery.flot.pie', 'jquery.flot.time', 'jquery.flot.fillbelow', 'jquery.flot.crosshair',
+  'jquery.flot.stack', 'jquery.flot.selection', 'jquery.flot.stackpercent', 'jquery.flot.events'
+];
+for (let flotDep of flotDeps) {
   exposeToPlugin(flotDep, {fakeDep: 1});
 }
 

+ 1 - 1
public/app/partials/login.html

@@ -20,7 +20,7 @@
       <form name="loginForm" class="login-form gf-form-group" ng-hide="disableLoginForm">
 				<div class="gf-form" ng-if="loginMode">
 					<span class="gf-form-label width-7">User</span>
-					<input type="text" name="username" class="gf-form-input max-width-14" required ng-model='formModel.user' placeholder={{loginHint}}>
+					<input type="text" name="username" class="gf-form-input max-width-14" required ng-model='formModel.user' placeholder={{loginHint}} autofocus>
 				</div>
 				<div class="gf-form" ng-if="loginMode">
 					<span class="gf-form-label width-7">Password</span>

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

@@ -41,7 +41,7 @@ function (angular, _, moment, dateMath, kbn, templatingVariable) {
         item.namespace = templateSrv.replace(item.namespace, options.scopedVars);
         item.metricName = templateSrv.replace(item.metricName, options.scopedVars);
         item.dimensions = self.convertDimensionFormat(item.dimensions, options.scopeVars);
-        item.period = self.getPeriod(item, options);
+        item.period = String(self.getPeriod(item, options)); // use string format for period in graph query, and alerting
 
         return _.extend({
           refId: item.refId,
@@ -318,6 +318,8 @@ function (angular, _, moment, dateMath, kbn, templatingVariable) {
 
       return this.getDimensionValues(region, namespace, metricName, 'ServiceName', dimensions).then(function () {
         return { status: 'success', message: 'Data source is working' };
+      }, function (err) {
+        return { status: 'error', message: err.message };
       });
     };
 

+ 2 - 2
public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts

@@ -37,7 +37,7 @@ describe('CloudWatchDatasource', function() {
             InstanceId: 'i-12345678'
           },
           statistics: ['Average'],
-          period: 300
+          period: '300'
         }
       ]
     };
@@ -109,7 +109,7 @@ describe('CloudWatchDatasource', function() {
 
       ctx.ds.query(query).then(function() {
         var params = requestParams.queries[0];
-        expect(params.period).to.be(600);
+        expect(params.period).to.be('600');
         done();
       });
       ctx.$rootScope.$apply();

+ 2 - 2
public/sass/_variables.light.scss

@@ -32,8 +32,8 @@ $white:            #fff;
 // -------------------------
 $blue:             	   	#2AB2E4;
 $blue-dark:             #3CAAD6;
-$green:                 #28B62C;
-$red:                   #FF4136;
+$green:                 #3aa655;
+$red:                   #d44939;
 $yellow:                #FF851B;
 $orange:                #Ff7941;
 $pink:                  #E671B8;

+ 1 - 0
public/sass/components/_gf-form.scss

@@ -125,6 +125,7 @@ $gf-form-margin: 3px;
   // text areas should be scrollable
   @at-root textarea#{&} {
     overflow: auto;
+    white-space: pre-wrap;
   }
 
   // Unstyle the caret on `<select>`s in IE10+.

+ 0 - 9
public/sass/components/_panel_graph.scss

@@ -211,15 +211,6 @@
     margin-right: 0px;
     line-height: initial;
   }
-  .close {
-    margin-right: 5px;
-    color: $link-color;
-    opacity: 0.7;
-    text-shadow: none;
-  }
-  .editor-row {
-    padding: 5px;
-  }
 }
 
 .annotation-tags {