From a4ddbad0abe687ce79d54303000a689bbd4b0c87 Mon Sep 17 00:00:00 2001 From: Frederic Branczyk <fbranczyk@gmail.com> Date: Thu, 20 Oct 2016 10:56:26 +0200 Subject: [PATCH] grafana-watcher: add datasource handling --- grafana-watcher/examples/grafana-bundle.yaml | 5 ++ .../examples/updated-configmap1.yaml | 2 + grafana-watcher/grafana/datasource.go | 76 +++++++++++++++++ grafana-watcher/grafana/grafana.go | 5 ++ grafana-watcher/main.go | 4 +- .../updater/{updater.go => dashboard.go} | 0 grafana-watcher/updater/datasource.go | 82 +++++++++++++++++++ 7 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 grafana-watcher/grafana/datasource.go rename grafana-watcher/updater/{updater.go => dashboard.go} (100%) create mode 100644 grafana-watcher/updater/datasource.go diff --git a/grafana-watcher/examples/grafana-bundle.yaml b/grafana-watcher/examples/grafana-bundle.yaml index d8099a98..ec9ca71d 100644 --- a/grafana-watcher/examples/grafana-bundle.yaml +++ b/grafana-watcher/examples/grafana-bundle.yaml @@ -43,6 +43,11 @@ spec: containers: - name: grafana image: grafana/grafana:3.1.1 + env: + - name: GF_AUTH_BASIC_ENABLED + value: "true" + - name: GF_AUTH_ANONYMOUS_ENABLED + value: "true" volumeMounts: - name: grafana-storage mountPath: /var/grafana-storage diff --git a/grafana-watcher/examples/updated-configmap1.yaml b/grafana-watcher/examples/updated-configmap1.yaml index 584a43f1..0550079b 100644 --- a/grafana-watcher/examples/updated-configmap1.yaml +++ b/grafana-watcher/examples/updated-configmap1.yaml @@ -7,3 +7,5 @@ data: {"dashboard":{"__inputs":[{"name":"DS_PROMETHEUS","label":"Prometheus","description":"","type":"datasource","pluginId":"prometheus","pluginName":"Prometheus"}],"__requires":[{"type":"panel","id":"singlestat","name":"Singlestat","version":""},{"type":"panel","id":"text","name":"Text","version":""},{"type":"panel","id":"graph","name":"Graph","version":""},{"type":"grafana","id":"grafana","name":"Grafana","version":"3.1.0"},{"type":"datasource","id":"prometheus","name":"Prometheus","version":"1.0.0"}],"id":null,"title":"Prometheus Stats","tags":[],"style":"dark","timezone":"browser","editable":true,"hideControls":true,"sharedCrosshair":false,"rows":[{"collapse":false,"editable":true,"height":178,"panels":[{"cacheTimeout":null,"colorBackground":false,"colorValue":false,"colors":["rgba(245, 54, 54, 0.9)","rgba(237, 129, 40, 0.89)","rgba(50, 172, 45, 0.97)"],"datasource":"${DS_PROMETHEUS}","decimals":1,"editable":true,"error":false,"format":"s","id":5,"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":false,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"expr":"(time() - process_start_time_seconds{job=\"prometheus\"})","intervalFactor":2,"refId":"A","step":4}],"thresholds":"","title":"Uptime","type":"singlestat","valueFontSize":"80%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"current","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1,"gauge":{"show":false,"minValue":0,"maxValue":100,"thresholdMarkers":true,"thresholdLabels":false}},{"cacheTimeout":null,"colorBackground":false,"colorValue":false,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_PROMETHEUS}","editable":true,"error":false,"format":"none","id":6,"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":false,"lineColor":"rgb(31, 120, 193)","show":true},"targets":[{"expr":"prometheus_local_storage_memory_series","intervalFactor":2,"refId":"A","step":4}],"thresholds":"1,5","title":"Local Storage Memory Series","type":"singlestat","valueFontSize":"70%","valueMaps":[],"valueName":"current","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1,"gauge":{"show":false,"minValue":0,"maxValue":100,"thresholdMarkers":true,"thresholdLabels":false}},{"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_PROMETHEUS}","editable":true,"error":false,"format":"none","id":7,"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":false,"lineColor":"rgb(31, 120, 193)","show":true},"targets":[{"expr":"prometheus_local_storage_indexing_queue_length","intervalFactor":2,"refId":"A","step":4}],"thresholds":"500,4000","title":"Interal Storage Queue Length","type":"singlestat","valueFontSize":"70%","valueMaps":[{"op":"=","text":"Empty","value":"0"}],"valueName":"current","mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"rangeMaps":[{"from":"null","to":"null","text":"N/A"}],"mappingType":1,"gauge":{"show":false,"minValue":0,"maxValue":100,"thresholdMarkers":true,"thresholdLabels":false}},{"content":"<img src=\"http://prometheus.io/assets/prometheus_logo_grey.svg\" alt=\"Prometheus logo\" style=\"height: 40px;\">\n<span style=\"font-family: 'Open Sans', 'Helvetica Neue', Helvetica; font-size: 25px;vertical-align: text-top;color: #bbbfc2;margin-left: 10px;\">Prometheus</span>\n\n<p style=\"margin-top: 10px;\">You're using Prometheus, an open-source systems monitoring and alerting toolkit originally built at SoundCloud. For more information, check out the <a href=\"http://www.grafana.org/\">Grafana</a> and <a href=\"http://prometheus.io/\">Prometheus</a> projects.</p>","editable":true,"error":false,"id":9,"links":[],"mode":"html","span":3,"style":{},"title":"","transparent":true,"type":"text"}],"title":"New row"},{"collapse":false,"editable":true,"height":227,"panels":[{"aliasColors":{"prometheus":"#C15C17","{instance=\"localhost:9090\",job=\"prometheus\"}":"#C15C17"},"bars":false,"datasource":"${DS_PROMETHEUS}","editable":true,"error":false,"fill":1,"grid":{"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":3,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":2,"links":[],"nullPointMode":"connected","percentage":false,"pointradius":2,"points":false,"renderer":"flot","seriesOverrides":[],"span":9,"stack":false,"steppedLine":false,"targets":[{"expr":"rate(prometheus_local_storage_ingested_samples_total[5m])","interval":"","intervalFactor":2,"legendFormat":"{{job}}","metric":"","refId":"A","step":2}],"timeFrom":null,"timeShift":null,"title":"Samples ingested (rate-5m)","tooltip":{"shared":true,"value_type":"cumulative","ordering":"alphabetical","msResolution":false},"type":"graph","yaxes":[{"show":true,"min":null,"max":null,"logBase":1,"format":"short"},{"show":true,"min":null,"max":null,"logBase":1,"format":"short"}],"xaxis":{"show":true}},{"content":"#### Samples Ingested\nThis graph displays the count of samples ingested by the Prometheus server, as measured over the last 5 minutes, per time series in the range vector. When troubleshooting an issue on IRC or Github, this is often the first stat requested by the Prometheus team. ","editable":true,"error":false,"id":8,"links":[],"mode":"markdown","span":2.995914043583536,"style":{},"title":"","transparent":true,"type":"text"}],"title":"New row"},{"collapse":false,"editable":true,"height":"250px","panels":[{"aliasColors":{"prometheus":"#F9BA8F","{instance=\"localhost:9090\",interval=\"5s\",job=\"prometheus\"}":"#F9BA8F"},"bars":false,"datasource":"${DS_PROMETHEUS}","editable":true,"error":false,"fill":1,"grid":{"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":2,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":2,"links":[],"nullPointMode":"connected","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"span":5,"stack":false,"steppedLine":false,"targets":[{"expr":"rate(prometheus_target_interval_length_seconds_count[5m])","intervalFactor":2,"legendFormat":"{{job}}","refId":"A","step":2}],"timeFrom":null,"timeShift":null,"title":"Target Scrapes (last 5m)","tooltip":{"shared":true,"value_type":"cumulative","ordering":"alphabetical","msResolution":false},"type":"graph","yaxes":[{"show":true,"min":null,"max":null,"logBase":1,"format":"short"},{"show":true,"min":null,"max":null,"logBase":1,"format":"short"}],"xaxis":{"show":true}},{"aliasColors":{},"bars":false,"datasource":"${DS_PROMETHEUS}","editable":true,"error":false,"fill":1,"grid":{"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":14,"legend":{"avg":false,"current":false,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":2,"links":[],"nullPointMode":"connected","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"span":4,"stack":false,"steppedLine":false,"targets":[{"expr":"prometheus_target_interval_length_seconds{quantile!=\"0.01\", quantile!=\"0.05\"}","interval":"","intervalFactor":2,"legendFormat":"{{quantile}} ({{interval}})","metric":"","refId":"A","step":2}],"timeFrom":null,"timeShift":null,"title":"Scrape Duration","tooltip":{"shared":true,"value_type":"cumulative","ordering":"alphabetical","msResolution":false},"type":"graph","yaxes":[{"show":true,"min":null,"max":null,"logBase":1,"format":"short"},{"show":true,"min":null,"max":null,"logBase":1,"format":"short"}],"xaxis":{"show":true}},{"content":"#### Scrapes\nPrometheus scrapes metrics from instrumented jobs, either directly or via an intermediary push gateway for short-lived jobs. Target scrapes will show how frequently targets are scraped, as measured over the last 5 minutes, per time series in the range vector. Scrape Duration will show how long the scrapes are taking, with percentiles available as series. ","editable":true,"error":false,"id":11,"links":[],"mode":"markdown","span":3,"style":{},"title":"","transparent":true,"type":"text"}],"title":"New row"},{"collapse":false,"editable":true,"height":"250px","panels":[{"aliasColors":{},"bars":false,"datasource":"${DS_PROMETHEUS}","decimals":null,"editable":true,"error":false,"fill":1,"grid":{"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":12,"legend":{"alignAsTable":false,"avg":false,"current":false,"hideEmpty":true,"max":false,"min":false,"show":true,"total":false,"values":false},"lines":true,"linewidth":2,"links":[],"nullPointMode":"connected","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"span":9,"stack":false,"steppedLine":false,"targets":[{"expr":"prometheus_evaluator_duration_milliseconds{quantile!=\"0.01\", quantile!=\"0.05\"}","interval":"","intervalFactor":2,"legendFormat":"{{quantile}}","refId":"A","step":2}],"timeFrom":null,"timeShift":null,"title":"Rule Eval Duration","tooltip":{"shared":true,"value_type":"cumulative","ordering":"alphabetical","msResolution":false},"type":"graph","yaxes":[{"show":true,"min":null,"max":null,"logBase":1,"format":"percentunit","label":""},{"show":true,"min":null,"max":null,"logBase":1,"format":"short"}],"xaxis":{"show":true}},{"content":"#### Rule Evaluation Duration\nThis graph panel plots the duration for all evaluations to execute. The 50th percentile, 90th percentile and 99th percentile are shown as three separate series to help identify outliers that may be skewing the data.","editable":true,"error":false,"id":15,"links":[],"mode":"markdown","span":3,"style":{},"title":"","transparent":true,"type":"text"}],"title":"New row"}],"time":{"from":"now-5m","to":"now"},"timepicker":{"now":true,"refresh_intervals":["5s","10s","30s","1m","5m","15m","30m","1h","2h","1d"],"time_options":["5m","15m","1h","6h","12h","24h","2d","7d","30d"]},"templating":{"list":[]},"annotations":{"list":[]},"refresh":false,"schemaVersion":12,"version":0,"links":[{"icon":"info","tags":[],"targetBlank":true,"title":"Grafana Docs","tooltip":"","type":"link","url":"http://www.grafana.org/docs"},{"icon":"info","tags":[],"targetBlank":true,"title":"Prometheus Docs","type":"link","url":"http://prometheus.io/docs/introduction/overview/"}],"gnetId":2,"description":"The official, pre-built Prometheus Stats Dashboard."},"overwrite":true,"inputs":[{"name":"DS_PROMETHEUS","type":"datasource","pluginId":"prometheus","value":"prometheus"}]} grafana-net-162-dashboard.json: | {"dashboard":{"__inputs":[{"name":"DS_PROMETHEUS","label":"Prometheus","description":"","type":"datasource","pluginId":"prometheus","pluginName":"Prometheus"}],"__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":"prometheus","name":"Prometheus","version":"1.0.0"}],"id":null,"title":"Kubernetes cluster monitoring (via Prometheus)","tags":["kubernetes"],"style":"dark","timezone":"browser","editable":true,"hideControls":true,"sharedCrosshair":true,"rows":[{"collapse":false,"editable":true,"height":"250px","panels":[{"cacheTimeout":null,"colorBackground":false,"colorValue":false,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_PROMETHEUS}","editable":true,"error":false,"format":"percent","gauge":{"maxValue":100,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":4,"interval":null,"isNew":true,"links":[],"mappingType":1,"mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","rangeMaps":[{"from":"null","text":"N/A","to":"null"}],"span":4,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":false,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"expr":"(sum(node_memory_MemTotal) - sum(node_memory_MemFree+node_memory_Buffers+node_memory_Cached) ) / sum(node_memory_MemTotal) * 100","interval":"10s","intervalFactor":1,"refId":"A","step":10}],"thresholds":"65, 90","title":"Cluster memory usage","type":"singlestat","valueFontSize":"80%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"current"},{"cacheTimeout":null,"colorBackground":false,"colorValue":false,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_PROMETHEUS}","decimals":2,"editable":true,"error":false,"format":"percent","gauge":{"maxValue":100,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":6,"interval":null,"isNew":true,"links":[],"mappingType":1,"mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","rangeMaps":[{"from":"null","text":"N/A","to":"null"}],"span":4,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":false,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"expr":"sum(sum by (io_kubernetes_container_name)( rate(container_cpu_usage_seconds_total{image!=\"\"}[1m] ) )) / count(node_cpu{mode=\"system\"}) * 100","interval":"10s","intervalFactor":1,"refId":"A","step":10}],"thresholds":"65, 90","title":"Cluster CPU usage","type":"singlestat","valueFontSize":"80%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"current"},{"cacheTimeout":null,"colorBackground":false,"colorValue":false,"colors":["rgba(50, 172, 45, 0.97)","rgba(237, 129, 40, 0.89)","rgba(245, 54, 54, 0.9)"],"datasource":"${DS_PROMETHEUS}","decimals":2,"editable":true,"error":false,"format":"percent","gauge":{"maxValue":100,"minValue":0,"show":true,"thresholdLabels":false,"thresholdMarkers":true},"id":7,"interval":null,"isNew":true,"links":[],"mappingType":1,"mappingTypes":[{"name":"value to text","value":1},{"name":"range to text","value":2}],"maxDataPoints":100,"nullPointMode":"connected","nullText":null,"postfix":"","postfixFontSize":"50%","prefix":"","prefixFontSize":"50%","rangeMaps":[{"from":"null","text":"N/A","to":"null"}],"span":4,"sparkline":{"fillColor":"rgba(31, 118, 189, 0.18)","full":false,"lineColor":"rgb(31, 120, 193)","show":false},"targets":[{"expr":"(sum(node_filesystem_size{device=\"rootfs\"}) - sum(node_filesystem_free{device=\"rootfs\"}) ) / sum(node_filesystem_size{device=\"rootfs\"}) * 100","interval":"10s","intervalFactor":1,"metric":"","refId":"A","step":10}],"thresholds":"65, 90","title":"Cluster Filesystem usage","type":"singlestat","valueFontSize":"80%","valueMaps":[{"op":"=","text":"N/A","value":"null"}],"valueName":"current"}],"title":"Row"},{"collapse":false,"editable":true,"height":"250px","panels":[{"aliasColors":{},"bars":false,"datasource":"${DS_PROMETHEUS}","decimals":3,"editable":true,"error":false,"fill":0,"grid":{"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":3,"isNew":true,"legend":{"alignAsTable":true,"avg":true,"current":true,"max":false,"min":false,"rightSide":true,"show":true,"sort":"current","sortDesc":true,"total":false,"values":true},"lines":true,"linewidth":2,"links":[],"nullPointMode":"connected","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"span":12,"stack":false,"steppedLine":false,"targets":[{"expr":"sum by (io_kubernetes_container_name)( rate(container_cpu_usage_seconds_total{image!=\"\"}[1m] ) )","interval":"10s","intervalFactor":1,"legendFormat":"{{ io_kubernetes_container_name }}","metric":"container_cpu","refId":"A","step":10}],"timeFrom":null,"timeShift":null,"title":"Pod CPU usage","tooltip":{"msResolution":true,"shared":true,"sort":0,"value_type":"cumulative"},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"percent","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}]}],"title":"New row"},{"collapse":false,"editable":true,"height":"250px","panels":[{"aliasColors":{},"bars":false,"datasource":"${DS_PROMETHEUS}","decimals":2,"editable":true,"error":false,"fill":0,"grid":{"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":2,"isNew":true,"legend":{"alignAsTable":true,"avg":true,"current":true,"max":false,"min":false,"rightSide":true,"show":true,"sideWidth":200,"sort":"current","sortDesc":true,"total":false,"values":true},"lines":true,"linewidth":2,"links":[],"nullPointMode":"connected","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"span":12,"stack":false,"steppedLine":false,"targets":[{"expr":"sort_desc(sum(container_memory_usage_bytes{image!=\"\"}) by (io_kubernetes_container_name, image))","interval":"10s","intervalFactor":1,"legendFormat":"{{ io_kubernetes_container_name }}","metric":"container_memory_usage:sort_desc","refId":"A","step":10}],"timeFrom":null,"timeShift":null,"title":"Pod memory usage","tooltip":{"msResolution":false,"shared":true,"sort":0,"value_type":"cumulative"},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"bytes","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}]},{"aliasColors":{},"bars":false,"datasource":"${DS_PROMETHEUS}","decimals":2,"editable":true,"error":false,"fill":0,"grid":{"threshold1":null,"threshold1Color":"rgba(216, 200, 27, 0.27)","threshold2":null,"threshold2Color":"rgba(234, 112, 112, 0.22)"},"id":8,"isNew":true,"legend":{"alignAsTable":true,"avg":true,"current":true,"max":false,"min":false,"rightSide":true,"show":true,"sideWidth":200,"sort":"current","sortDesc":true,"total":false,"values":true},"lines":true,"linewidth":2,"links":[],"nullPointMode":"connected","percentage":false,"pointradius":5,"points":false,"renderer":"flot","seriesOverrides":[],"span":12,"stack":false,"steppedLine":false,"targets":[{"expr":"sort_desc(sum by (kubernetes_pod_name) (rate (container_network_receive_bytes_total{name!=\"\", kubernetes_pod_name=~\"op-.*\"}[1m]) ))","interval":"10s","intervalFactor":1,"legendFormat":"{{ kubernetes_pod_name }}","metric":"network","refId":"A","step":10},{"expr":"sort_desc(sum by (kubernetes_pod_name) (rate (container_network_transmit_bytes_total{name!=\"\", kubernetes_pod_name=~\"op-.*\"}[1m]) ))","interval":"10s","intervalFactor":1,"legendFormat":"{{ kubernetes_pod_name }}","metric":"network","refId":"B","step":10}],"timeFrom":null,"timeShift":null,"title":"Pod Network i/o","tooltip":{"msResolution":false,"shared":true,"sort":0,"value_type":"cumulative"},"type":"graph","xaxis":{"show":true},"yaxes":[{"format":"bytes","label":null,"logBase":1,"max":null,"min":null,"show":true},{"format":"short","label":null,"logBase":1,"max":null,"min":null,"show":true}]}],"title":"New row"}],"time":{"from":"now-1h","to":"now"},"timepicker":{"refresh_intervals":["5s","10s","30s","1m","5m","15m","30m","1h","2h","1d"],"time_options":["5m","15m","1h","6h","12h","24h","2d","7d","30d"]},"templating":{"list":[]},"annotations":{"list":[]},"refresh":"10s","schemaVersion":12,"version":46,"links":[],"gnetId":162,"description":"Monitor a Kubernetes cluster using Prometheus TSDB. Shows overall cluster CPU / Memory / Disk usage as well as individual pod statistics. "},"overwrite":true,"inputs":[{"name":"DS_PROMETHEUS","type":"datasource","pluginId":"prometheus","value":"prometheus"}]} + prometheus-datasource.json: | + {"name":"prometheus","type":"prometheus","url":"http://prometheus:9090","access":"proxy","basicAuth":false} diff --git a/grafana-watcher/grafana/datasource.go b/grafana-watcher/grafana/datasource.go new file mode 100644 index 00000000..28ece285 --- /dev/null +++ b/grafana-watcher/grafana/datasource.go @@ -0,0 +1,76 @@ +package grafana + +import ( + "encoding/json" + "io" + "net/http" + "strconv" +) + +type DatasourcesInterface interface { + All() ([]GrafanaDatasource, error) + Create(datasourceJson io.Reader) error + Delete(id int) error +} + +// DatasourcesClient is an implementation of the DatasourcesInterface. The +// datasources HTTP API of Grafana requires admin access. +type DatasourcesClient struct { + BaseUrl string + HTTPClient *http.Client +} + +type GrafanaDatasource struct { + Id int `json:"id"` + Name string `json:"name"` +} + +func NewDatasourcesClient(baseUrl string, c *http.Client) DatasourcesInterface { + return &DatasourcesClient{ + BaseUrl: baseUrl, + HTTPClient: c, + } +} + +func (c *DatasourcesClient) All() ([]GrafanaDatasource, error) { + allUrl := c.BaseUrl + "/api/datasources" + resp, err := c.HTTPClient.Get(allUrl) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + datasources := make([]GrafanaDatasource, 0) + + err = json.NewDecoder(resp.Body).Decode(&datasources) + if err != nil { + return nil, err + } + + return datasources, nil +} + +func (c *DatasourcesClient) Delete(id int) error { + deleteUrl := c.BaseUrl + "/api/datasources/" + strconv.Itoa(id) + req, err := http.NewRequest("DELETE", deleteUrl, nil) + if err != nil { + return err + } + + _, err = c.HTTPClient.Do(req) + if err != nil { + return err + } + + return nil +} + +func (c *DatasourcesClient) Create(datasourceJson io.Reader) error { + createUrl := c.BaseUrl + "/api/datasources" + _, err := c.HTTPClient.Post(createUrl, "application/json", datasourceJson) + if err != nil { + return err + } + + return nil +} diff --git a/grafana-watcher/grafana/grafana.go b/grafana-watcher/grafana/grafana.go index 93117d4c..803d8f01 100644 --- a/grafana-watcher/grafana/grafana.go +++ b/grafana-watcher/grafana/grafana.go @@ -6,6 +6,7 @@ import ( type Interface interface { Dashboards() DashboardsInterface + Datasources() DatasourcesInterface } type Clientset struct { @@ -23,3 +24,7 @@ func New(baseUrl string) Interface { func (c *Clientset) Dashboards() DashboardsInterface { return NewDashboardsClient(c.BaseUrl, c.HTTPClient) } + +func (c *Clientset) Datasources() DatasourcesInterface { + return NewDatasourcesClient(c.BaseUrl, c.HTTPClient) +} diff --git a/grafana-watcher/main.go b/grafana-watcher/main.go index 41e9aa73..34096705 100644 --- a/grafana-watcher/main.go +++ b/grafana-watcher/main.go @@ -86,10 +86,10 @@ func main() { g := grafana.New(*grafanaUrl) du := updater.NewGrafanaDashboardUpdater(g.Dashboards(), filepath.Join(*watchDir, "*-dashboard.json")) - //su := updater.NewGrafanaSourceUpdater(g.Sources(), filepath.Join(*watchDir, "*-source.json")) + su := updater.NewGrafanaDatasourceUpdater(g.Datasources(), filepath.Join(*watchDir, "*-datasource.json")) w := newVolumeWatcher(*watchDir) w.AddEventHandler(du) - //w.AddEventHandler(su) + w.AddEventHandler(su) w.Run() } diff --git a/grafana-watcher/updater/updater.go b/grafana-watcher/updater/dashboard.go similarity index 100% rename from grafana-watcher/updater/updater.go rename to grafana-watcher/updater/dashboard.go diff --git a/grafana-watcher/updater/datasource.go b/grafana-watcher/updater/datasource.go new file mode 100644 index 00000000..ad96852c --- /dev/null +++ b/grafana-watcher/updater/datasource.go @@ -0,0 +1,82 @@ +package updater + +import ( + "log" + "os" + "path/filepath" + + "github.com/coreos/kube-prometheus/grafana-watcher/grafana" +) + +type GrafanaDatasourceUpdater struct { + client grafana.DatasourcesInterface + glob string +} + +func NewGrafanaDatasourceUpdater(c grafana.DatasourcesInterface, g string) Updater { + return &GrafanaDatasourceUpdater{ + client: c, + glob: g, + } +} + +func (u *GrafanaDatasourceUpdater) OnModify() error { + err := u.deleteAllDatasources() + if err != nil { + return err + } + err = u.createDatasourcesFromFiles() + if err != nil { + return err + } + + return nil +} + +func (u *GrafanaDatasourceUpdater) deleteAllDatasources() error { + log.Println("Retrieving existing datasources") + datasources, err := u.client.All() + if err != nil { + return err + } + + log.Printf("Deleting %d datasources\n", len(datasources)) + for _, d := range datasources { + log.Println("Deleting datasource:", d.Id) + + err := u.client.Delete(d.Id) + if err != nil { + return err + } + } + + return nil +} + +func (u *GrafanaDatasourceUpdater) createDatasourcesFromFiles() error { + filePaths, err := filepath.Glob(u.glob) + if err != nil { + return err + } + + for _, fp := range filePaths { + u.createDatasourceFromFile(fp) + if err != nil { + return err + } + } + + return nil +} + +func (u *GrafanaDatasourceUpdater) createDatasourceFromFile(filePath string) error { + log.Println("Creating datasource from:", filePath) + + f, err := os.Open(filePath) + if err != nil { + return err + } + defer f.Close() + + return u.client.Create(f) +} -- GitLab