Merge remote-tracking branch 'grafana/master' into alpha-react-table

* grafana/master:
  Fixed styling of gicon's in dropdown menus
  cleanup plugin versions
  use explore icon
  fix comments
  typescript functions on replace
  remove console.log
  add ScopedVars to replace function
  Expose onQueryChange to angular plugins
  fix allow anonymous initial bind for ldap search
This commit is contained in:
ryan
2019-03-06 00:59:27 -08:00
26 changed files with 164 additions and 47 deletions
+2 -1
View File
@@ -1,8 +1,9 @@
import { ComponentClass } from 'react';
import { TimeSeries, LoadingState, TableData } from './data';
import { TimeRange } from './time';
import { ScopedVars } from './datasource';
export type InterpolateFunction = (value: string, format?: string | Function) => string;
export type InterpolateFunction = (value: string, scopedVars?: ScopedVars, format?: string | Function) => string;
export interface PanelProps<T = any> {
panelData: PanelData;
+12 -1
View File
@@ -18,6 +18,7 @@ import (
type ILdapConn interface {
Bind(username, password string) error
UnauthenticatedBind(username string) error
Search(*ldap.SearchRequest) (*ldap.SearchResult, error)
StartTLS(*tls.Config) error
Close()
@@ -259,7 +260,17 @@ func (a *ldapAuther) initialBind(username, userPassword string) error {
bindPath = fmt.Sprintf(a.server.BindDN, username)
}
if err := a.conn.Bind(bindPath, userPassword); err != nil {
bindFn := func() error {
return a.conn.Bind(bindPath, userPassword)
}
if userPassword == "" {
bindFn = func() error {
return a.conn.UnauthenticatedBind(bindPath)
}
}
if err := bindFn(); err != nil {
a.log.Info("Initial bind failed", "error", err)
if ldapErr, ok := err.(*ldap.Error); ok {
+81 -3
View File
@@ -13,6 +13,70 @@ import (
)
func TestLdapAuther(t *testing.T) {
Convey("initialBind", t, func() {
Convey("Given bind dn and password configured", func() {
conn := &mockLdapConn{}
var actualUsername, actualPassword string
conn.bindProvider = func(username, password string) error {
actualUsername = username
actualPassword = password
return nil
}
ldapAuther := &ldapAuther{
conn: conn,
server: &LdapServerConf{
BindDN: "cn=%s,o=users,dc=grafana,dc=org",
BindPassword: "bindpwd",
},
}
err := ldapAuther.initialBind("user", "pwd")
So(err, ShouldBeNil)
So(ldapAuther.requireSecondBind, ShouldBeTrue)
So(actualUsername, ShouldEqual, "cn=user,o=users,dc=grafana,dc=org")
So(actualPassword, ShouldEqual, "bindpwd")
})
Convey("Given bind dn configured", func() {
conn := &mockLdapConn{}
var actualUsername, actualPassword string
conn.bindProvider = func(username, password string) error {
actualUsername = username
actualPassword = password
return nil
}
ldapAuther := &ldapAuther{
conn: conn,
server: &LdapServerConf{
BindDN: "cn=%s,o=users,dc=grafana,dc=org",
},
}
err := ldapAuther.initialBind("user", "pwd")
So(err, ShouldBeNil)
So(ldapAuther.requireSecondBind, ShouldBeFalse)
So(actualUsername, ShouldEqual, "cn=user,o=users,dc=grafana,dc=org")
So(actualPassword, ShouldEqual, "pwd")
})
Convey("Given empty bind dn and password", func() {
conn := &mockLdapConn{}
unauthenticatedBindWasCalled := false
var actualUsername string
conn.unauthenticatedBindProvider = func(username string) error {
unauthenticatedBindWasCalled = true
actualUsername = username
return nil
}
ldapAuther := &ldapAuther{
conn: conn,
server: &LdapServerConf{},
}
err := ldapAuther.initialBind("user", "pwd")
So(err, ShouldBeNil)
So(ldapAuther.requireSecondBind, ShouldBeTrue)
So(unauthenticatedBindWasCalled, ShouldBeTrue)
So(actualUsername, ShouldBeEmpty)
})
})
Convey("When translating ldap user to grafana user", t, func() {
@@ -365,12 +429,26 @@ func TestLdapAuther(t *testing.T) {
}
type mockLdapConn struct {
result *ldap.SearchResult
searchCalled bool
searchAttributes []string
result *ldap.SearchResult
searchCalled bool
searchAttributes []string
bindProvider func(username, password string) error
unauthenticatedBindProvider func(username string) error
}
func (c *mockLdapConn) Bind(username, password string) error {
if c.bindProvider != nil {
return c.bindProvider(username, password)
}
return nil
}
func (c *mockLdapConn) UnauthenticatedBind(username string) error {
if c.unauthenticatedBindProvider != nil {
return c.unauthenticatedBindProvider(username)
}
return nil
}
@@ -0,0 +1,35 @@
import { PanelChrome } from './PanelChrome';
jest.mock('sass/_variables.generated.scss', () => ({
panelhorizontalpadding: 10,
panelVerticalPadding: 10,
}));
describe('PanelChrome', () => {
let chrome: PanelChrome;
beforeEach(() => {
chrome = new PanelChrome({
panel: {
scopedVars: {
aaa: { value: 'AAA', text: 'upperA' },
bbb: { value: 'BBB', text: 'upperB' },
},
},
dashboard: {},
plugin: {},
isFullscreen: false,
});
});
it('Should replace a panel variable', () => {
const out = chrome.replaceVariables('hello $aaa');
expect(out).toBe('hello AAA');
});
it('But it should prefer the local variable value', () => {
const extra = { aaa: { text: '???', value: 'XXX' } };
const out = chrome.replaceVariables('hello $aaa and $bbb', extra);
expect(out).toBe('hello XXX and BBB');
});
});
@@ -19,6 +19,7 @@ import { profiler } from 'app/core/profiler';
import { DashboardModel, PanelModel } from '../state';
import { PanelPlugin } from 'app/types';
import { DataQueryResponse, TimeRange, LoadingState, PanelData, DataQueryError } from '@grafana/ui';
import { ScopedVars } from '@grafana/ui';
import variables from 'sass/_variables.generated.scss';
import templateSrv from 'app/features/templating/template_srv';
@@ -85,8 +86,12 @@ export class PanelChrome extends PureComponent<Props, State> {
});
};
replaceVariables = (value: string, format?: string) => {
return templateSrv.replace(value, this.props.panel.scopedVars, format);
replaceVariables = (value: string, extraVars?: ScopedVars, format?: string) => {
let vars = this.props.panel.scopedVars;
if (extraVars) {
vars = vars ? { ...vars, ...extraVars } : extraVars;
}
return templateSrv.replace(value, vars, format);
};
onDataResponse = (dataQueryResponse: DataQueryResponse) => {
@@ -43,6 +43,9 @@ export default class QueryEditor extends PureComponent<QueryEditorProps, any> {
this.props.onQueryChange(target);
this.props.onExecuteQuery();
},
onQueryChange: () => {
this.props.onQueryChange(target);
},
events: exploreEvents,
panel: { datasource, targets: [target] },
dashboard: {},
@@ -224,7 +224,7 @@ class MetricsPanelCtrl extends PanelCtrl {
items.push({
text: 'Explore',
click: 'ctrl.explore();',
icon: 'fa fa-fw fa-rocket',
icon: 'gicon gicon-explore',
shortcut: 'x',
});
}
@@ -25,7 +25,7 @@
</div>
<aside class="page-sidebar">
<section class="page-sidebar-section">
<section class="page-sidebar-section" ng-if="ctrl.model.info.version">
<h4>Version</h4>
<span>{{ctrl.model.info.version}}</span>
<div ng-show="ctrl.model.hasUpdate">
@@ -54,7 +54,7 @@
</li>
</ul>
</section>
<section class="page-sidebar-section">
<section class="page-sidebar-section" ng-if="ctrl.model.info.links">
<h5>Links</h4>
<ul class="ui-list">
<li ng-repeat="link in ctrl.model.info.links">
@@ -1,7 +1,7 @@
import kbn from 'app/core/utils/kbn';
import _ from 'lodash';
import { variableRegex } from 'app/features/templating/variable';
import { TimeRange } from '@grafana/ui/src';
import { TimeRange, ScopedVars } from '@grafana/ui/src';
function luceneEscape(value) {
return value.replace(/([\!\*\+\-\=<>\s\&\|\(\)\[\]\{\}\^\~\?\:\\/"])/g, '\\$1');
@@ -220,7 +220,7 @@ export class TemplateSrv {
return values;
}
replace(target, scopedVars?, format?) {
replace(target: string, scopedVars?: ScopedVars, format?: string | Function) {
if (!target) {
return target;
}
@@ -16,7 +16,6 @@
"logos": {
"small": "img/amazon-web-services.png",
"large": "img/amazon-web-services.png"
},
"version": "5.0.0"
}
}
}
@@ -14,10 +14,7 @@
"small": "img/elasticsearch.svg",
"large": "img/elasticsearch.svg"
},
"links": [
{"name": "elastic.co", "url": "https://www.elastic.co/products/elasticsearch"}
],
"version": "5.0.0"
"links": [{ "name": "elastic.co", "url": "https://www.elastic.co/products/elasticsearch" }]
},
"alerting": true,
@@ -31,7 +31,6 @@
"name": "Graphite 1.1 Release",
"url": "https://grafana.com/blog/2018/01/11/graphite-1.1-teaching-an-old-dog-new-tricks/"
}
],
"version": "5.0.0"
]
}
}
@@ -22,7 +22,6 @@
"logos": {
"small": "img/influxdb_logo.svg",
"large": "img/influxdb_logo.svg"
},
"version": "5.0.0"
}
}
}
@@ -12,8 +12,7 @@
"logos": {
"small": "img/mysql_logo.svg",
"large": "img/mysql_logo.svg"
},
"version": "5.0.0"
}
},
"alerting": true,
@@ -18,7 +18,6 @@
"logos": {
"small": "img/opentsdb_logo.png",
"large": "img/opentsdb_logo.png"
},
"version": "5.0.0"
}
}
}
@@ -12,8 +12,7 @@
"logos": {
"small": "img/postgresql_logo.svg",
"large": "img/postgresql_logo.svg"
},
"version": "5.0.0"
}
},
"alerting": true,
@@ -42,7 +42,6 @@
"name": "Prometheus",
"url": "https://prometheus.io/"
}
],
"version": "5.0.0"
]
}
}
@@ -14,7 +14,6 @@
"logos": {
"small": "img/icn-singlestat-panel.svg",
"large": "img/icn-singlestat-panel.svg"
},
"version": "5.0.0"
}
}
}
@@ -14,7 +14,6 @@
"logos": {
"small": "img/icn-dashlist-panel.svg",
"large": "img/icn-dashlist-panel.svg"
},
"version": "5.0.0"
}
}
}
+1 -2
View File
@@ -14,7 +14,6 @@
"logos": {
"small": "img/icn-graph-panel.svg",
"large": "img/icn-graph-panel.svg"
},
"version": "5.0.0"
}
}
}
+1 -2
View File
@@ -18,7 +18,6 @@
"links": [
{ "name": "Brendan Gregg - Heatmaps", "url": "http://www.brendangregg.com/heatmaps.html" },
{ "name": "Brendan Gregg - Latency Heatmaps", "url": " http://www.brendangregg.com/HeatMaps/latency.html" }
],
"version": "5.0.0"
]
}
}
@@ -14,7 +14,6 @@
"logos": {
"small": "img/icn-dashlist-panel.svg",
"large": "img/icn-dashlist-panel.svg"
},
"version": "5.0.0"
}
}
}
@@ -14,7 +14,6 @@
"logos": {
"small": "img/icn-singlestat-panel.svg",
"large": "img/icn-singlestat-panel.svg"
},
"version": "5.0.0"
}
}
}
+1 -2
View File
@@ -14,7 +14,6 @@
"logos": {
"small": "img/icn-table-panel.svg",
"large": "img/icn-table-panel.svg"
},
"version": "5.0.0"
}
}
}
+1 -2
View File
@@ -13,7 +13,6 @@
"logos": {
"small": "img/icn-text-panel.svg",
"large": "img/icn-text-panel.svg"
},
"version": "5.0.0"
}
}
}
+3 -1
View File
@@ -93,7 +93,9 @@
}
.gicon {
opacity: 0.9;
opacity: 0.7;
width: 14px;
height: 14px;
}
}
}