diff --git a/CHANGELOG.md b/CHANGELOG.md index ec9a9293b23..7a75ad758c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ ## Fixes * **Gzip**: Fixes bug gravatar images when gzip was enabled [#5952](https://github.com/grafana/grafana/issues/5952) +* **Alert list**: Now shows alert state changes even after adding manual annotations on dashboard [#9951](https://github.com/grafana/grafana/issues/9951) # 4.6.2 (2017-11-16) diff --git a/docker/blocks/prometheus2/Dockerfile b/docker/blocks/prometheus2/Dockerfile new file mode 100644 index 00000000000..d4a9eb2d75d --- /dev/null +++ b/docker/blocks/prometheus2/Dockerfile @@ -0,0 +1,3 @@ +FROM prom/prometheus:v2.0.0 +ADD prometheus.yml /etc/prometheus/ +ADD alert.rules /etc/prometheus/ diff --git a/docker/blocks/prometheus2/alert.rules b/docker/blocks/prometheus2/alert.rules new file mode 100644 index 00000000000..563d1e89994 --- /dev/null +++ b/docker/blocks/prometheus2/alert.rules @@ -0,0 +1,10 @@ +# Alert Rules + +ALERT AppCrash + IF process_open_fds > 0 + FOR 15s + LABELS { severity="critical" } + ANNOTATIONS { + summary = "Number of open fds > 0", + description = "Just testing" + } diff --git a/docker/blocks/prometheus2/prometheus.yml b/docker/blocks/prometheus2/prometheus.yml new file mode 100644 index 00000000000..83dda78bb3c --- /dev/null +++ b/docker/blocks/prometheus2/prometheus.yml @@ -0,0 +1,35 @@ +# my global config +global: + scrape_interval: 10s # By default, scrape targets every 15 seconds. + evaluation_interval: 10s # By default, scrape targets every 15 seconds. + # scrape_timeout is set to the global default (10s). + +# Load and evaluate rules in this file every 'evaluation_interval' seconds. +#rule_files: +# - "alert.rules" +# - "first.rules" +# - "second.rules" + +# alerting: +# alertmanagers: +# - scheme: http +# static_configs: +# - targets: +# - "127.0.0.1:9093" + +scrape_configs: + - job_name: 'prometheus' + static_configs: + - targets: ['localhost:9090'] + + - job_name: 'node_exporter' + static_configs: + - targets: ['127.0.0.1:9100'] + + - job_name: 'fake-data-gen' + static_configs: + - targets: ['127.0.0.1:9091'] + + - job_name: 'grafana' + static_configs: + - targets: ['127.0.0.1:3000'] diff --git a/pkg/api/annotations.go b/pkg/api/annotations.go index 32a0a3035d3..0bf95557abc 100644 --- a/pkg/api/annotations.go +++ b/pkg/api/annotations.go @@ -22,6 +22,7 @@ func GetAnnotations(c *middleware.Context) Response { PanelId: c.QueryInt64("panelId"), Limit: c.QueryInt64("limit"), Tags: c.QueryStrings("tags"), + Type: c.Query("type"), } repo := annotations.GetRepository() diff --git a/pkg/api/http_server.go b/pkg/api/http_server.go index eadaa117e86..89456d20d8c 100644 --- a/pkg/api/http_server.go +++ b/pkg/api/http_server.go @@ -194,7 +194,8 @@ func (hs *HttpServer) metricsEndpoint(ctx *macaron.Context) { } func (hs *HttpServer) healthHandler(ctx *macaron.Context) { - if ctx.Req.Method != "GET" || ctx.Req.URL.Path != "/api/health" { + notHeadOrGet := ctx.Req.Method != http.MethodGet && ctx.Req.Method != http.MethodHead + if notHeadOrGet || ctx.Req.URL.Path != "/api/health" { return } diff --git a/pkg/services/annotations/annotations.go b/pkg/services/annotations/annotations.go index 2fdc824f172..02f927a76ba 100644 --- a/pkg/services/annotations/annotations.go +++ b/pkg/services/annotations/annotations.go @@ -17,6 +17,7 @@ type ItemQuery struct { DashboardId int64 `json:"dashboardId"` PanelId int64 `json:"panelId"` Tags []string `json:"tags"` + Type string `json:"type"` Limit int64 `json:"limit"` } diff --git a/pkg/services/sqlstore/annotation.go b/pkg/services/sqlstore/annotation.go index d97db10f630..effffb8bab4 100644 --- a/pkg/services/sqlstore/annotation.go +++ b/pkg/services/sqlstore/annotation.go @@ -158,6 +158,10 @@ func (r *SqlAnnotationRepo) Find(query *annotations.ItemQuery) ([]*annotations.I params = append(params, query.From, query.To) } + if query.Type == "alert" { + sql.WriteString(` AND annotation.alert_id > 0`) + } + if len(query.Tags) > 0 { keyValueFilters := []string{} diff --git a/pkg/services/sqlstore/annotation_test.go b/pkg/services/sqlstore/annotation_test.go index e1902b63fa8..2afd4479b66 100644 --- a/pkg/services/sqlstore/annotation_test.go +++ b/pkg/services/sqlstore/annotation_test.go @@ -42,6 +42,7 @@ func TestAnnotations(t *testing.T) { UserId: 1, DashboardId: 1, Text: "hello", + Type: "alert", Epoch: 10, Tags: []string{"outage", "error", "type:outage", "server:server-1"}, } @@ -91,6 +92,19 @@ func TestAnnotations(t *testing.T) { So(items, ShouldHaveLength, 0) }) + Convey("Should not find one when type filter does not match", func() { + items, err := repo.Find(&annotations.ItemQuery{ + OrgId: 1, + DashboardId: 1, + From: 1, + To: 15, + Type: "alert", + }) + + So(err, ShouldBeNil) + So(items, ShouldHaveLength, 0) + }) + Convey("Should find one when all tag filters does match", func() { items, err := repo.Find(&annotations.ItemQuery{ OrgId: 1, diff --git a/pkg/services/sqlstore/dashboard_test.go b/pkg/services/sqlstore/dashboard_test.go index e6734fd5ee3..b8f915f7245 100644 --- a/pkg/services/sqlstore/dashboard_test.go +++ b/pkg/services/sqlstore/dashboard_test.go @@ -381,10 +381,11 @@ func TestDashboardDataAccess(t *testing.T) { childDash2 := insertTestDashboard("child dash 2", 1, folder2.Id, false, "prod") currentUser := createUser("viewer", "Viewer", false) + var rootFolderId int64 = 0 Convey("and one folder is expanded, the other collapsed", func() { Convey("should return dashboards in root and expanded folder", func() { - query := &search.FindPersistedDashboardsQuery{FolderIds: []int64{folder1.Id}, SignedInUser: &m.SignedInUser{UserId: currentUser.Id, OrgId: 1}, OrgId: 1} + query := &search.FindPersistedDashboardsQuery{FolderIds: []int64{rootFolderId, folder1.Id}, SignedInUser: &m.SignedInUser{UserId: currentUser.Id, OrgId: 1}, OrgId: 1} err := SearchDashboards(query) So(err, ShouldBeNil) So(len(query.Result), ShouldEqual, 4) diff --git a/pkg/tsdb/mysql/mysql.go b/pkg/tsdb/mysql/mysql.go index bdb48867b6e..e5c6b92f245 100644 --- a/pkg/tsdb/mysql/mysql.go +++ b/pkg/tsdb/mysql/mysql.go @@ -35,7 +35,7 @@ func NewMysqlQueryEndpoint(datasource *models.DataSource) (tsdb.TsdbQueryEndpoin MacroEngine: NewMysqlMacroEngine(), } - cnnstr := fmt.Sprintf("%s:%s@%s(%s)/%s?collation=utf8mb4_unicode_ci&parseTime=true&loc=UTC", + cnnstr := fmt.Sprintf("%s:%s@%s(%s)/%s?collation=utf8mb4_unicode_ci&parseTime=true&loc=UTC&allowNativePasswords=true", datasource.User, datasource.Password, "tcp", diff --git a/public/app/core/directives/metric_segment.js b/public/app/core/directives/metric_segment.js index da21a5b3c45..2754f8d8c6e 100644 --- a/public/app/core/directives/metric_segment.js +++ b/public/app/core/directives/metric_segment.js @@ -48,7 +48,10 @@ function (_, $, coreModule) { segment.html = selected.html || selected.value; segment.fake = false; segment.expandable = selected.expandable; - segment.type = selected.type; + + if (selected.type) { + segment.type = selected.type; + } } else if (segment.custom !== 'false') { segment.value = value; diff --git a/public/app/core/services/search_srv.ts b/public/app/core/services/search_srv.ts index 4b32c2735bf..3d85cb20005 100644 --- a/public/app/core/services/search_srv.ts +++ b/public/app/core/services/search_srv.ts @@ -52,7 +52,7 @@ export class SearchSrv { } search(options) { - if (!options.query) { + if (!options.query && !options.tag) { return this.browse(); } diff --git a/public/app/features/dashboard/dashboard_list_ctrl.ts b/public/app/features/dashboard/dashboard_list_ctrl.ts index 032e8d7ffa0..ace7d8065a7 100644 --- a/public/app/features/dashboard/dashboard_list_ctrl.ts +++ b/public/app/features/dashboard/dashboard_list_ctrl.ts @@ -5,6 +5,7 @@ import { SearchSrv } from 'app/core/services/search_srv'; export class DashboardListCtrl { public sections: any []; tags: any []; + selectedTagFilter: any; query: any; navModel: any; canDelete = false; @@ -15,27 +16,40 @@ export class DashboardListCtrl { this.navModel = navModelSrv.getNav('dashboards', 'dashboards'); this.query = {query: '', mode: 'tree', tag: []}; - this.getDashboards(); - // this.getDashboards().then(() => { - // this.getTags(); - // }); + this.getDashboards().then(() => { + this.getTags(); + }); } getDashboards() { - return this.searchSrv.browse().then((result) => { + if (this.query.query.length === 0 && this.query.tag.length === 0) { + return this.searchSrv.browse().then((result) => { + return this.initDashboardList(result); + }); + } - this.sections = result; - - for (let section of this.sections) { - section.checked = false; - - for (let dashboard of section.items) { - dashboard.checked = false; - } - } + return this.searchSrv.search(this.query).then((result) => { + return this.initDashboardList(result); }); } + initDashboardList(result: any) { + if (!result) { + this.sections = []; + return; + } + + this.sections = result; + + for (let section of this.sections) { + section.checked = false; + + for (let dashboard of section.items) { + dashboard.checked = false; + } + } + } + selectionChanged() { let selectedDashboards = 0; @@ -119,11 +133,16 @@ export class DashboardListCtrl { }); } - // getTags() { - // return this.backendSrv.get('/api/dashboards/tags').then((results) => { - // this.tags = results; - // }); - // } + toggleFolder(section) { + return this.searchSrv.toggleFolder(section); + } + + getTags() { + return this.searchSrv.getDashboardTags().then((results) => { + this.tags = [{ term: 'Filter By Tag', disabled: true }].concat(results); + this.selectedTagFilter = this.tags[0]; + }); + } filterByTag(tag, evt) { this.query.tag.push(tag); @@ -134,6 +153,12 @@ export class DashboardListCtrl { } } + filterChange() { + this.query.tag.push(this.selectedTagFilter.term); + this.selectedTagFilter = this.tags[0]; + this.getDashboards(); + } + removeTag(tag, evt) { this.query.tag = _.without(this.query.tag, tag); this.getDashboards(); diff --git a/public/app/features/dashboard/partials/dashboardList.html b/public/app/features/dashboard/partials/dashboardList.html index 104861af637..b5f0fabeef4 100644 --- a/public/app/features/dashboard/partials/dashboardList.html +++ b/public/app/features/dashboard/partials/dashboardList.html @@ -54,23 +54,31 @@
- +
+