Merge branch 'master' of github.com:grafana/grafana
This commit is contained in:
+3
-1
@@ -2,9 +2,10 @@
|
||||
|
||||
### New Features
|
||||
|
||||
* **Alerting**: Option to disable OK alert notifications [#12330](https://github.com/grafana/grafana/issues/12330) & [#6696](https://github.com/grafana/grafana/issues/6696), thx [@davewat](https://github.com/davewat)
|
||||
* **Postgres/MySQL/MSSQL**: Adds support for configuration of max open/idle connections and connection max lifetime. Also, panels with multiple SQL queries will now be executed concurrently [#11711](https://github.com/grafana/grafana/issues/11711), thx [@connection-reset](https://github.com/connection-reset)
|
||||
* **MSSQL**: Add encrypt setting to allow configuration of how data sent between client and server are encrypted [#13629](https://github.com/grafana/grafana/issues/13629), thx [@ramiro](https://github.com/ramiro)
|
||||
* **Alerting**: Option to disable OK alert notifications [#12330](https://github.com/grafana/grafana/issues/12330) & [#6696](https://github.com/grafana/grafana/issues/6696), thx [@davewat](https://github.com/davewat)
|
||||
* **MySQL**: Support connecting thru Unix socket for MySQL datasource [#12342](https://github.com/grafana/grafana/issues/12342), thx [@Yukinoshita-Yukino](https://github.com/Yukinoshita-Yukino)
|
||||
|
||||
### Minor
|
||||
|
||||
@@ -19,6 +20,7 @@
|
||||
# 5.3.2 (unreleased)
|
||||
|
||||
* **Postgres**: Fix template variables error [#13692](https://github.com/grafana/grafana/issues/13692), thx [@svenklemm](https://github.com/svenklemm)
|
||||
* **Cloudwatch**: Fix service panic because of race conditions [#13674](https://github.com/grafana/grafana/issues/13674), thx [@mtanda](https://github.com/mtanda)
|
||||
|
||||
# 5.3.1 (2018-10-16)
|
||||
|
||||
|
||||
@@ -69,15 +69,27 @@ bra run
|
||||
|
||||
Open grafana in your browser (default: `http://localhost:3000`) and login with admin user (default: `user/pass = admin/admin`).
|
||||
|
||||
### Building a docker image (on linux/amd64)
|
||||
### Building a Docker image
|
||||
|
||||
This builds a docker image from your local sources:
|
||||
There are two different ways to build a Grafana docker image. If you're machine is setup for Grafana development and you run linux/amd64 you can build just the image. Otherwise, there is the option to build Grafana completely within Docker.
|
||||
|
||||
Run the image you have built using: `docker run --rm -p 3000:3000 grafana/grafana:dev`
|
||||
|
||||
#### Building on linux/amd64 (fast)
|
||||
|
||||
1. Build the frontend `go run build.go build-frontend`
|
||||
2. Build the docker image `make build-docker-dev`
|
||||
|
||||
The resulting image will be tagged as `grafana/grafana:dev`
|
||||
|
||||
#### Building anywhere (slower)
|
||||
|
||||
Choose this option to build on platforms other than linux/amd64 and/or not have to setup the Grafana development environment.
|
||||
|
||||
1. `make build-docker-full` or `docker build -t grafana/grafana:dev .`
|
||||
|
||||
The resulting image will be tagged as `grafana/grafana:dev`
|
||||
|
||||
### Dev config
|
||||
|
||||
Create a custom.ini in the conf directory to override default configuration options.
|
||||
@@ -113,18 +125,6 @@ GRAFANA_TEST_DB=mysql go test ./pkg/...
|
||||
GRAFANA_TEST_DB=postgres go test ./pkg/...
|
||||
```
|
||||
|
||||
## Building custom docker image
|
||||
|
||||
You can build a custom image using Docker, which doesn't require installing any dependencies besides docker itself.
|
||||
```bash
|
||||
git clone https://github.com/grafana/grafana
|
||||
cd grafana
|
||||
docker build -t grafana:dev .
|
||||
docker run -d --name=grafana -p 3000:3000 grafana:dev
|
||||
```
|
||||
|
||||
Open grafana in your browser (default: `http://localhost:3000`) and login with admin user (default: `user/pass = admin/admin`).
|
||||
|
||||
## Contribute
|
||||
|
||||
If you have any idea for an improvement or found a bug, do not hesitate to open an issue.
|
||||
|
||||
@@ -160,6 +160,7 @@
|
||||
"react-redux": "^5.0.7",
|
||||
"react-select": "2.1.0",
|
||||
"react-sizeme": "^2.3.6",
|
||||
"react-table": "^6.8.6",
|
||||
"react-transition-group": "^2.2.1",
|
||||
"redux": "^4.0.0",
|
||||
"redux-logger": "^3.0.6",
|
||||
|
||||
+3
-1
@@ -185,7 +185,9 @@ func (a *ldapAuther) GetGrafanaUserFor(ctx *m.ReqContext, ldapUser *LdapUserInfo
|
||||
|
||||
if ldapUser.isMemberOf(group.GroupDN) {
|
||||
extUser.OrgRoles[group.OrgId] = group.OrgRole
|
||||
extUser.IsGrafanaAdmin = group.IsGrafanaAdmin
|
||||
if extUser.IsGrafanaAdmin == nil || *extUser.IsGrafanaAdmin == false {
|
||||
extUser.IsGrafanaAdmin = group.IsGrafanaAdmin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,9 +86,10 @@ func (e *CloudWatchExecutor) Query(ctx context.Context, dsInfo *models.DataSourc
|
||||
}
|
||||
|
||||
func (e *CloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, queryContext *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
||||
result := &tsdb.Response{
|
||||
results := &tsdb.Response{
|
||||
Results: make(map[string]*tsdb.QueryResult),
|
||||
}
|
||||
resultChan := make(chan *tsdb.QueryResult, len(queryContext.Queries))
|
||||
|
||||
eg, ectx := errgroup.WithContext(ctx)
|
||||
|
||||
@@ -102,10 +103,10 @@ func (e *CloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, queryCo
|
||||
RefId := queryContext.Queries[i].RefId
|
||||
query, err := parseQuery(queryContext.Queries[i].Model)
|
||||
if err != nil {
|
||||
result.Results[RefId] = &tsdb.QueryResult{
|
||||
results.Results[RefId] = &tsdb.QueryResult{
|
||||
Error: err,
|
||||
}
|
||||
return result, nil
|
||||
return results, nil
|
||||
}
|
||||
query.RefId = RefId
|
||||
|
||||
@@ -118,10 +119,10 @@ func (e *CloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, queryCo
|
||||
}
|
||||
|
||||
if query.Id == "" && query.Expression != "" {
|
||||
result.Results[query.RefId] = &tsdb.QueryResult{
|
||||
results.Results[query.RefId] = &tsdb.QueryResult{
|
||||
Error: fmt.Errorf("Invalid query: id should be set if using expression"),
|
||||
}
|
||||
return result, nil
|
||||
return results, nil
|
||||
}
|
||||
|
||||
eg.Go(func() error {
|
||||
@@ -130,12 +131,13 @@ func (e *CloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, queryCo
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
result.Results[query.RefId] = &tsdb.QueryResult{
|
||||
resultChan <- &tsdb.QueryResult{
|
||||
RefId: query.RefId,
|
||||
Error: err,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
result.Results[queryRes.RefId] = queryRes
|
||||
resultChan <- queryRes
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -149,10 +151,10 @@ func (e *CloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, queryCo
|
||||
return err
|
||||
}
|
||||
for _, queryRes := range queryResponses {
|
||||
result.Results[queryRes.RefId] = queryRes
|
||||
if err != nil {
|
||||
result.Results[queryRes.RefId].Error = err
|
||||
queryRes.Error = err
|
||||
}
|
||||
resultChan <- queryRes
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -162,8 +164,12 @@ func (e *CloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, queryCo
|
||||
if err := eg.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
close(resultChan)
|
||||
for result := range resultChan {
|
||||
results.Results[result.RefId] = result
|
||||
}
|
||||
|
||||
return result, nil
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (e *CloudWatchExecutor) executeQuery(ctx context.Context, query *CloudWatchQuery, queryContext *tsdb.TsdbQuery) (*tsdb.QueryResult, error) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/go-xorm/core"
|
||||
@@ -20,10 +21,14 @@ func init() {
|
||||
func newMysqlQueryEndpoint(datasource *models.DataSource) (tsdb.TsdbQueryEndpoint, error) {
|
||||
logger := log.New("tsdb.mysql")
|
||||
|
||||
protocol := "tcp"
|
||||
if strings.HasPrefix(datasource.Url, "/") {
|
||||
protocol = "unix"
|
||||
}
|
||||
cnnstr := fmt.Sprintf("%s:%s@%s(%s)/%s?collation=utf8mb4_unicode_ci&parseTime=true&loc=UTC&allowNativePasswords=true",
|
||||
datasource.User,
|
||||
datasource.Password,
|
||||
"tcp",
|
||||
protocol,
|
||||
datasource.Url,
|
||||
datasource.Database,
|
||||
)
|
||||
|
||||
@@ -644,7 +644,9 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
||||
/>
|
||||
)}
|
||||
{supportsTable && showingTable ? (
|
||||
<Table className="m-t-3" data={tableResult} loading={loading} onClickCell={this.onClickTableCell} />
|
||||
<div className="panel-container">
|
||||
<Table data={tableResult} loading={loading} onClickCell={this.onClickTableCell} />
|
||||
</div>
|
||||
) : null}
|
||||
{supportsLogs && showingLogs ? <Logs data={logsResult} loading={loading} /> : null}
|
||||
</main>
|
||||
|
||||
@@ -1,84 +1,55 @@
|
||||
import _ from 'lodash';
|
||||
import React, { PureComponent } from 'react';
|
||||
import ReactTable from 'react-table';
|
||||
|
||||
import TableModel from 'app/core/table_model';
|
||||
|
||||
const EMPTY_TABLE = new TableModel();
|
||||
|
||||
interface TableProps {
|
||||
className?: string;
|
||||
data: TableModel;
|
||||
loading: boolean;
|
||||
onClickCell?: (columnKey: string, rowValue: string) => void;
|
||||
}
|
||||
|
||||
interface SFCCellProps {
|
||||
columnIndex: number;
|
||||
onClickCell?: (columnKey: string, rowValue: string, columnIndex: number, rowIndex: number, table: TableModel) => void;
|
||||
rowIndex: number;
|
||||
table: TableModel;
|
||||
value: string;
|
||||
function prepareRows(rows, columnNames) {
|
||||
return rows.map(cells => _.zipObject(columnNames, cells));
|
||||
}
|
||||
|
||||
function Cell(props: SFCCellProps) {
|
||||
const { columnIndex, rowIndex, table, value, onClickCell } = props;
|
||||
const column = table.columns[columnIndex];
|
||||
if (column && column.filterable && onClickCell) {
|
||||
const onClick = event => {
|
||||
event.preventDefault();
|
||||
onClickCell(column.text, value, columnIndex, rowIndex, table);
|
||||
export default class Table extends PureComponent<TableProps> {
|
||||
getCellProps = (state, rowInfo, column) => {
|
||||
return {
|
||||
onClick: () => {
|
||||
const columnKey = column.Header;
|
||||
const rowValue = rowInfo.row[columnKey];
|
||||
this.props.onClickCell(columnKey, rowValue);
|
||||
},
|
||||
};
|
||||
return (
|
||||
<td>
|
||||
<a className="link" onClick={onClick}>
|
||||
{value}
|
||||
</a>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
return <td>{value}</td>;
|
||||
}
|
||||
};
|
||||
|
||||
export default class Table extends PureComponent<TableProps, {}> {
|
||||
render() {
|
||||
const { className = '', data, loading, onClickCell } = this.props;
|
||||
const { data, loading } = this.props;
|
||||
const tableModel = data || EMPTY_TABLE;
|
||||
if (!loading && data && data.rows.length === 0) {
|
||||
return (
|
||||
<table className={`${className} filter-table`}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Table</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="muted">The queries returned no data for a table.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
const columnNames = tableModel.columns.map(({ text }) => text);
|
||||
const columns = tableModel.columns.map(({ filterable, text }) => ({
|
||||
Header: text,
|
||||
accessor: text,
|
||||
show: text !== 'Time',
|
||||
Cell: row => <span className={filterable ? 'link' : ''}>{row.value}</span>,
|
||||
}));
|
||||
const noDataText = data ? 'The queries returned no data for a table.' : '';
|
||||
|
||||
return (
|
||||
<table className={`${className} filter-table`}>
|
||||
<thead>
|
||||
<tr>{tableModel.columns.map(col => <th key={col.text}>{col.text}</th>)}</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{tableModel.rows.map((row, i) => (
|
||||
<tr key={i}>
|
||||
{row.map((value, j) => (
|
||||
<Cell
|
||||
key={j}
|
||||
columnIndex={j}
|
||||
rowIndex={i}
|
||||
value={String(value)}
|
||||
table={data}
|
||||
onClickCell={onClickCell}
|
||||
/>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<ReactTable
|
||||
columns={columns}
|
||||
data={tableModel.rows}
|
||||
getTdProps={this.getCellProps}
|
||||
loading={loading}
|
||||
minRows={0}
|
||||
noDataText={noDataText}
|
||||
resolveData={data => prepareRows(data, columnNames)}
|
||||
showPagination={data}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
// vendor
|
||||
// DEPENDENCIES
|
||||
@import '../../node_modules/react-table/react-table.css';
|
||||
|
||||
// VENDOR
|
||||
@import '../vendor/css/timepicker.css';
|
||||
@import '../vendor/css/spectrum.css';
|
||||
@import '../vendor/css/rc-cascader.scss';
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
font-size: $font-size-root;
|
||||
font-family: $font-family-monospace;
|
||||
height: auto;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.slate-query-field-wrapper {
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
}
|
||||
|
||||
.query-row-tools {
|
||||
width: 6rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.query-row-field {
|
||||
@@ -186,3 +186,60 @@
|
||||
margin: 0.25em 0.5em 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
// ReactTable basic overrides (does not include pivot/groups/filters)
|
||||
// When integrating ReactTable as new panel plugin, move to _panel_table.scss
|
||||
|
||||
.ReactTable {
|
||||
border: none;
|
||||
// Allow some space for the no-data text
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
.ReactTable .rt-thead.-header {
|
||||
box-shadow: none;
|
||||
background: $list-item-bg;
|
||||
border-top: 2px solid $body-bg;
|
||||
border-bottom: 2px solid $body-bg;
|
||||
height: 2em;
|
||||
}
|
||||
.ReactTable .rt-thead.-header .rt-th {
|
||||
text-align: left;
|
||||
color: $blue;
|
||||
font-weight: 500;
|
||||
}
|
||||
.ReactTable .rt-thead .rt-td,
|
||||
.ReactTable .rt-thead .rt-th {
|
||||
padding: 0.45em 0 0.45em 1.1em;
|
||||
border-right: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.ReactTable .rt-tbody .rt-td {
|
||||
padding: 0.45em 0 0.45em 1.1em;
|
||||
border-bottom: 2px solid $body-bg;
|
||||
border-right: 2px solid $body-bg;
|
||||
}
|
||||
.ReactTable .rt-tbody .rt-td:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
.ReactTable .-pagination .-btn {
|
||||
color: $blue;
|
||||
background: $list-item-bg;
|
||||
}
|
||||
.ReactTable .-pagination input,
|
||||
.ReactTable .-pagination select {
|
||||
color: $input-color;
|
||||
background-color: $input-bg;
|
||||
}
|
||||
.ReactTable .-loading {
|
||||
background: $input-bg;
|
||||
}
|
||||
.ReactTable .-loading.-active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.ReactTable .-loading > div {
|
||||
color: $input-color;
|
||||
}
|
||||
.ReactTable .rt-tr .rt-td:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user