Commit 84b15fc7 by Alexander Zobnin

Merge branch 'master' into heatmap-panel

parents 65a829b6 085c4c56
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
* **InfluxDB**: Small fix for the "glow" when focus the field for LIMIT and SLIMIT [#7799](https://github.com/grafana/grafana/pull/7799) thx [@thuck](https://github.com/thuck) * **InfluxDB**: Small fix for the "glow" when focus the field for LIMIT and SLIMIT [#7799](https://github.com/grafana/grafana/pull/7799) thx [@thuck](https://github.com/thuck)
* **Panels**: Delay loading & Lazy load panels as they become visible (scrolled into view) [#5216](https://github.com/grafana/grafana/issues/5216) thx [@jifwin](https://github.com/jifwin) * **Panels**: Delay loading & Lazy load panels as they become visible (scrolled into view) [#5216](https://github.com/grafana/grafana/issues/5216) thx [@jifwin](https://github.com/jifwin)
* **Graph**: Support auto grid min/max when using log scale [#3090](https://github.com/grafana/grafana/issues/3090), thx [@bigbenhur](https://github.com/bigbenhur) * **Graph**: Support auto grid min/max when using log scale [#3090](https://github.com/grafana/grafana/issues/3090), thx [@bigbenhur](https://github.com/bigbenhur)
* **Elasticsearch**: Support histogram aggregations [#3164](https://github.com/grafana/grafana/issues/3164)
## Minor Enchancements ## Minor Enchancements
......
...@@ -74,7 +74,6 @@ password = ...@@ -74,7 +74,6 @@ password =
url = url =
# Max conn setting default is 0 (mean not set) # Max conn setting default is 0 (mean not set)
max_conn =
max_idle_conn = max_idle_conn =
max_open_conn = max_open_conn =
...@@ -206,6 +205,9 @@ default_theme = dark ...@@ -206,6 +205,9 @@ default_theme = dark
# Set to true to disable (hide) the login form, useful if you use OAuth # Set to true to disable (hide) the login form, useful if you use OAuth
disable_login_form = false disable_login_form = false
# Set to true to disable the signout link in the side menu. useful if you use auth.proxy
disable_signout_menu = false
#################################### Anonymous Auth ###################### #################################### Anonymous Auth ######################
[auth.anonymous] [auth.anonymous]
# enable anonymous access # enable anonymous access
......
...@@ -83,7 +83,6 @@ ...@@ -83,7 +83,6 @@
;path = grafana.db ;path = grafana.db
# Max conn setting default is 0 (mean not set) # Max conn setting default is 0 (mean not set)
;max_conn =
;max_idle_conn = ;max_idle_conn =
;max_open_conn = ;max_open_conn =
...@@ -193,6 +192,9 @@ ...@@ -193,6 +192,9 @@
# Set to true to disable (hide) the login form, useful if you use OAuth, defaults to false # Set to true to disable (hide) the login form, useful if you use OAuth, defaults to false
;disable_login_form = false ;disable_login_form = false
# Set to true to disable the signout link in the side menu. useful if you use auth.proxy, defaults to false
;disable_signout_menu = false
#################################### Anonymous Auth ########################## #################################### Anonymous Auth ##########################
[auth.anonymous] [auth.anonymous]
# enable anonymous access # enable anonymous access
......
...@@ -27,7 +27,7 @@ notifications will be sent out when the rule conditions are met. ...@@ -27,7 +27,7 @@ notifications will be sent out when the rule conditions are met.
This feature has been worked on for over a year with many iterations and rewrites This feature has been worked on for over a year with many iterations and rewrites
just to make sure the foundations are really solid. We are really proud to finally release it! just to make sure the foundations are really solid. We are really proud to finally release it!
Since the alerting execution is processed in the backend all data source plugins are not supported. Since the alerting execution is processed in the backend not all data source plugins are supported.
Right now Graphite, Prometheus, InfluxDB and OpenTSDB are supported. Elasticsearch is being worked Right now Graphite, Prometheus, InfluxDB and OpenTSDB are supported. Elasticsearch is being worked
on but will be not ready for v4 release. on but will be not ready for v4 release.
...@@ -70,8 +70,8 @@ to add graph comments in the form of annotations directly from within Grafana in ...@@ -70,8 +70,8 @@ to add graph comments in the form of annotations directly from within Grafana in
{{< imgbox max-width="30%" img="/img/docs/v4/alert_list_panel.png" caption="Alert List Panel" >}} {{< imgbox max-width="30%" img="/img/docs/v4/alert_list_panel.png" caption="Alert List Panel" >}}
This new panel allows you to show alert rules or a history of alert rule state changes. You can filter based on states your This new panel allows you to show alert rules or a history of alert rule state changes. You can filter based on states you are
interested in. Very useful panel for overview style dashboards. interested in. This panel is very useful for overview style dashboards.
<div class="clearfix"></div> <div class="clearfix"></div>
...@@ -79,8 +79,8 @@ interested in. Very useful panel for overview style dashboards. ...@@ -79,8 +79,8 @@ interested in. Very useful panel for overview style dashboards.
{{< imgbox max-width="30%" img="/img/docs/v4/adhoc_filters.gif" caption="Ad-hoc filters variable" >}} {{< imgbox max-width="30%" img="/img/docs/v4/adhoc_filters.gif" caption="Ad-hoc filters variable" >}}
This is a new and very different type of template variable. It will allow you to create new key/value filters on the fly. This is a new and very different type of template variable. It will allow you to create new key/value filters on the fly
With autocomplete for both key and values. The filter condition will be automatically applied to all with autocomplete for both key and values. The filter condition will be automatically applied to all
queries that use that data source. This feature opens up more exploratory dashboards. In the gif animation to the right queries that use that data source. This feature opens up more exploratory dashboards. In the gif animation to the right
you have a dashboard for Elasticsearch log data. It uses one query variable that allow you to quickly change how the data you have a dashboard for Elasticsearch log data. It uses one query variable that allow you to quickly change how the data
is grouped, and an interval variable for controlling the granularity of the time buckets. What was missing is grouped, and an interval variable for controlling the granularity of the time buckets. What was missing
...@@ -126,15 +126,15 @@ We always try to bring some UX/UI refinements & polish in every release. ...@@ -126,15 +126,15 @@ We always try to bring some UX/UI refinements & polish in every release.
{{< imgbox max-width="50%" img="/img/docs/v4/add_panel.gif" caption="Add Panel flow" >}} {{< imgbox max-width="50%" img="/img/docs/v4/add_panel.gif" caption="Add Panel flow" >}}
We spent a lot of time improving the dashboard building experience. Trying to make it both We spent a lot of time improving the dashboard building experience to make it both
more efficient and easier for beginners. After many good but not great experiments more efficient and easier for beginners. After many good but not great experiments
with a `build mode` we eventually decided to just improve the green row menu and with a `build mode` we eventually decided to just improve the green row menu and
continue work on a `build mode` for a future release. continue work on a `build mode` for a future release.
The new row menu automatically slides out when you mouse over the edge of the row. You no longer need The new row menu automatically slides out when you mouse over the edge of the row. You no longer need
to hover over the small green icon and the click it to expand the row menu. to hover over the small green icon and then click it to expand the row menu.
There is some minor improvements to drag and drop behaviour. Now when dragging a panel from one row There are some minor improvements to drag and drop behaviour. Now when dragging a panel from one row
to another you will insert the panel and Grafana will automatically make room for it. to another you will insert the panel and Grafana will automatically make room for it.
When you drag a panel within a row you will simply reorder the panels. When you drag a panel within a row you will simply reorder the panels.
...@@ -150,7 +150,7 @@ We plan to further improve dashboard building in the future with a more rich gri ...@@ -150,7 +150,7 @@ We plan to further improve dashboard building in the future with a more rich gri
{{< imgbox max-width="40%" img="/img/docs/v4/shortcuts.png" caption="Shortcuts" >}} {{< imgbox max-width="40%" img="/img/docs/v4/shortcuts.png" caption="Shortcuts" >}}
Grafana v4 introduces a number of really powerful keyboard shortcuts. You can now focus a panel Grafana v4 introduces a number of really powerful keyboard shortcuts. You can now focus a panel
by hovering over it with your mouse. With a panel focused you can simple hit `e` to toggle panel by hovering over it with your mouse. With a panel focused you can simply hit `e` to toggle panel
edit mode, or `v` to toggle fullscreen mode. `p r` removes the panel. `p s` opens share edit mode, or `v` to toggle fullscreen mode. `p r` removes the panel. `p s` opens share
modal. modal.
...@@ -164,10 +164,10 @@ Some nice navigation shortcuts are: ...@@ -164,10 +164,10 @@ Some nice navigation shortcuts are:
## Upgrade & Breaking changes ## Upgrade & Breaking changes
There are no breaking changes. Old dashboards and features should work the same. Grafana-server will automatically upgrade it's db There are no breaking changes. Old dashboards and features should work the same. Grafana-server will automatically upgrade its db
schema on restart. It's advisable to do a backup of Grafana's database before updating. schema on restart. It's advisable to do a backup of Grafana's database before updating.
If your are using plugins make sure to update your plugins as some might not work perfectly v4. If you are using plugins make sure to update your plugins as some might not work perfectly v4.
You can update plugins using grafana-cli You can update plugins using grafana-cli
......
...@@ -267,6 +267,10 @@ options are `Admin` and `Editor` and `Read Only Editor`. e.g. : ...@@ -267,6 +267,10 @@ options are `Admin` and `Editor` and `Read Only Editor`. e.g. :
Set to true to disable (hide) the login form, useful if you use OAuth, defaults to false. Set to true to disable (hide) the login form, useful if you use OAuth, defaults to false.
### disable_signout_menu
Set to true to disable the signout link in the side menu. useful if you use auth.proxy, defaults to false.
<hr> <hr>
## [auth.anonymous] ## [auth.anonymous]
...@@ -427,6 +431,11 @@ Set to `true` to enable LDAP integration (default: `false`) ...@@ -427,6 +431,11 @@ Set to `true` to enable LDAP integration (default: `false`)
### config_file ### config_file
Path to the LDAP specific configuration file (default: `/etc/grafana/ldap.toml`) Path to the LDAP specific configuration file (default: `/etc/grafana/ldap.toml`)
### allow_sign_up
Allow sign up should almost always be true (default) to allow new Grafana users to be created (if ldap authentication is ok). If set to
false only pre-existing Grafana users will be able to login (if ldap authentication is ok).
> For details on LDAP Configuration, go to the [LDAP Integration]({{< relref "ldap.md" >}}) page. > For details on LDAP Configuration, go to the [LDAP Integration]({{< relref "ldap.md" >}}) page.
<hr> <hr>
......
...@@ -133,16 +133,17 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro ...@@ -133,16 +133,17 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
} }
jsonObj := map[string]interface{}{ jsonObj := map[string]interface{}{
"defaultDatasource": defaultDatasource, "defaultDatasource": defaultDatasource,
"datasources": datasources, "datasources": datasources,
"panels": panels, "panels": panels,
"appSubUrl": setting.AppSubUrl, "appSubUrl": setting.AppSubUrl,
"allowOrgCreate": (setting.AllowUserOrgCreate && c.IsSignedIn) || c.IsGrafanaAdmin, "allowOrgCreate": (setting.AllowUserOrgCreate && c.IsSignedIn) || c.IsGrafanaAdmin,
"authProxyEnabled": setting.AuthProxyEnabled, "authProxyEnabled": setting.AuthProxyEnabled,
"ldapEnabled": setting.LdapEnabled, "ldapEnabled": setting.LdapEnabled,
"alertingEnabled": setting.AlertingEnabled, "alertingEnabled": setting.AlertingEnabled,
"googleAnalyticsId": setting.GoogleAnalyticsId, "googleAnalyticsId": setting.GoogleAnalyticsId,
"disableLoginForm": setting.DisableLoginForm, "disableLoginForm": setting.DisableLoginForm,
"disableSignoutMenu": setting.DisableSignoutMenu,
"buildInfo": map[string]interface{}{ "buildInfo": map[string]interface{}{
"version": setting.BuildVersion, "version": setting.BuildVersion,
"commit": setting.BuildCommit, "commit": setting.BuildCommit,
......
...@@ -116,6 +116,7 @@ func (this *SlackNotifier) Notify(evalContext *alerting.EvalContext) error { ...@@ -116,6 +116,7 @@ func (this *SlackNotifier) Notify(evalContext *alerting.EvalContext) error {
body := map[string]interface{}{ body := map[string]interface{}{
"attachments": []map[string]interface{}{ "attachments": []map[string]interface{}{
{ {
"fallback": evalContext.GetNotificationTitle(),
"color": evalContext.GetStateModel().Color, "color": evalContext.GetStateModel().Color,
"title": evalContext.GetNotificationTitle(), "title": evalContext.GetNotificationTitle(),
"title_link": ruleUrl, "title_link": ruleUrl,
......
...@@ -50,7 +50,7 @@ func addAlertMigrations(mg *Migrator) { ...@@ -50,7 +50,7 @@ func addAlertMigrations(mg *Migrator) {
Columns: []*Column{ Columns: []*Column{
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "org_id", Type: DB_BigInt, Nullable: false}, {Name: "org_id", Type: DB_BigInt, Nullable: false},
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "name", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "type", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "type", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "settings", Type: DB_Text, Nullable: false}, {Name: "settings", Type: DB_Text, Nullable: false},
{Name: "created", Type: DB_DateTime, Nullable: false}, {Name: "created", Type: DB_DateTime, Nullable: false},
...@@ -67,4 +67,19 @@ func addAlertMigrations(mg *Migrator) { ...@@ -67,4 +67,19 @@ func addAlertMigrations(mg *Migrator) {
})) }))
mg.AddMigration("add index alert_notification org_id & name", NewAddIndexMigration(alert_notification, alert_notification.Indices[0])) mg.AddMigration("add index alert_notification org_id & name", NewAddIndexMigration(alert_notification, alert_notification.Indices[0]))
mg.AddMigration("Update alert table charset", NewTableCharsetMigration("alert", []*Column{
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "message", Type: DB_Text, Nullable: false},
{Name: "state", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "settings", Type: DB_Text, Nullable: false},
{Name: "severity", Type: DB_Text, Nullable: false},
{Name: "execution_error", Type: DB_Text, Nullable: false},
{Name: "eval_data", Type: DB_Text, Nullable: true},
}))
mg.AddMigration("Update alert_notification table charset", NewTableCharsetMigration("alert_notification", []*Column{
{Name: "name", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "type", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "settings", Type: DB_Text, Nullable: false},
}))
} }
...@@ -44,4 +44,14 @@ func addAnnotationMig(mg *Migrator) { ...@@ -44,4 +44,14 @@ func addAnnotationMig(mg *Migrator) {
mg.AddMigration("add index annotation 2 v3", NewAddIndexMigration(table, table.Indices[2])) mg.AddMigration("add index annotation 2 v3", NewAddIndexMigration(table, table.Indices[2]))
mg.AddMigration("add index annotation 3 v3", NewAddIndexMigration(table, table.Indices[3])) mg.AddMigration("add index annotation 3 v3", NewAddIndexMigration(table, table.Indices[3]))
mg.AddMigration("add index annotation 4 v3", NewAddIndexMigration(table, table.Indices[4])) mg.AddMigration("add index annotation 4 v3", NewAddIndexMigration(table, table.Indices[4]))
mg.AddMigration("Update annotation table charset", NewTableCharsetMigration("annotation", []*Column{
{Name: "type", Type: DB_NVarchar, Length: 25, Nullable: false},
{Name: "title", Type: DB_Text, Nullable: false},
{Name: "text", Type: DB_Text, Nullable: false},
{Name: "metric", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "prev_state", Type: DB_NVarchar, Length: 25, Nullable: false},
{Name: "new_state", Type: DB_NVarchar, Length: 25, Nullable: false},
{Name: "data", Type: DB_Text, Nullable: false},
}))
} }
...@@ -8,7 +8,7 @@ func addApiKeyMigrations(mg *Migrator) { ...@@ -8,7 +8,7 @@ func addApiKeyMigrations(mg *Migrator) {
Columns: []*Column{ Columns: []*Column{
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "account_id", Type: DB_BigInt, Nullable: false}, {Name: "account_id", Type: DB_BigInt, Nullable: false},
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "name", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "key", Type: DB_Varchar, Length: 64, Nullable: false}, {Name: "key", Type: DB_Varchar, Length: 64, Nullable: false},
{Name: "role", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "role", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "created", Type: DB_DateTime, Nullable: false}, {Name: "created", Type: DB_DateTime, Nullable: false},
...@@ -41,8 +41,8 @@ func addApiKeyMigrations(mg *Migrator) { ...@@ -41,8 +41,8 @@ func addApiKeyMigrations(mg *Migrator) {
Columns: []*Column{ Columns: []*Column{
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "org_id", Type: DB_BigInt, Nullable: false}, {Name: "org_id", Type: DB_BigInt, Nullable: false},
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "name", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "key", Type: DB_Varchar, Length: 255, Nullable: false}, {Name: "key", Type: DB_Varchar, Length: 190, Nullable: false},
{Name: "role", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "role", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "created", Type: DB_DateTime, Nullable: false}, {Name: "created", Type: DB_DateTime, Nullable: false},
{Name: "updated", Type: DB_DateTime, Nullable: false}, {Name: "updated", Type: DB_DateTime, Nullable: false},
...@@ -72,4 +72,10 @@ func addApiKeyMigrations(mg *Migrator) { ...@@ -72,4 +72,10 @@ func addApiKeyMigrations(mg *Migrator) {
})) }))
mg.AddMigration("Drop old table api_key_v1", NewDropTableMigration("api_key_v1")) mg.AddMigration("Drop old table api_key_v1", NewDropTableMigration("api_key_v1"))
mg.AddMigration("Update api_key table charset", NewTableCharsetMigration("api_key", []*Column{
{Name: "name", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "key", Type: DB_Varchar, Length: 190, Nullable: false},
{Name: "role", Type: DB_NVarchar, Length: 255, Nullable: false},
}))
} }
...@@ -8,7 +8,7 @@ func addDashboardMigration(mg *Migrator) { ...@@ -8,7 +8,7 @@ func addDashboardMigration(mg *Migrator) {
Columns: []*Column{ Columns: []*Column{
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "version", Type: DB_Int, Nullable: false}, {Name: "version", Type: DB_Int, Nullable: false},
{Name: "slug", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "slug", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "title", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "title", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "data", Type: DB_Text, Nullable: false}, {Name: "data", Type: DB_Text, Nullable: false},
{Name: "account_id", Type: DB_BigInt, Nullable: false}, {Name: "account_id", Type: DB_BigInt, Nullable: false},
...@@ -56,7 +56,7 @@ func addDashboardMigration(mg *Migrator) { ...@@ -56,7 +56,7 @@ func addDashboardMigration(mg *Migrator) {
Columns: []*Column{ Columns: []*Column{
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "version", Type: DB_Int, Nullable: false}, {Name: "version", Type: DB_Int, Nullable: false},
{Name: "slug", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "slug", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "title", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "title", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "data", Type: DB_Text, Nullable: false}, {Name: "data", Type: DB_Text, Nullable: false},
{Name: "org_id", Type: DB_BigInt, Nullable: false}, {Name: "org_id", Type: DB_BigInt, Nullable: false},
...@@ -125,4 +125,15 @@ func addDashboardMigration(mg *Migrator) { ...@@ -125,4 +125,15 @@ func addDashboardMigration(mg *Migrator) {
mg.AddMigration("Add index for dashboard_id in dashboard_tag", NewAddIndexMigration(dashboardTagV1, &Index{ mg.AddMigration("Add index for dashboard_id in dashboard_tag", NewAddIndexMigration(dashboardTagV1, &Index{
Cols: []string{"dashboard_id"}, Type: IndexType, Cols: []string{"dashboard_id"}, Type: IndexType,
})) }))
mg.AddMigration("Update dashboard table charset", NewTableCharsetMigration("dashboard", []*Column{
{Name: "slug", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "title", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "plugin_id", Type: DB_NVarchar, Nullable: true, Length: 255},
{Name: "data", Type: DB_MediumText, Nullable: false},
}))
mg.AddMigration("Update dashboard_tag table charset", NewTableCharsetMigration("dashboard_tag", []*Column{
{Name: "term", Type: DB_NVarchar, Length: 50, Nullable: false},
}))
} }
...@@ -8,7 +8,7 @@ func addDashboardSnapshotMigrations(mg *Migrator) { ...@@ -8,7 +8,7 @@ func addDashboardSnapshotMigrations(mg *Migrator) {
Columns: []*Column{ Columns: []*Column{
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "key", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "key", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "dashboard", Type: DB_Text, Nullable: false}, {Name: "dashboard", Type: DB_Text, Nullable: false},
{Name: "expires", Type: DB_DateTime, Nullable: false}, {Name: "expires", Type: DB_DateTime, Nullable: false},
{Name: "created", Type: DB_DateTime, Nullable: false}, {Name: "created", Type: DB_DateTime, Nullable: false},
...@@ -28,8 +28,8 @@ func addDashboardSnapshotMigrations(mg *Migrator) { ...@@ -28,8 +28,8 @@ func addDashboardSnapshotMigrations(mg *Migrator) {
Columns: []*Column{ Columns: []*Column{
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "key", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "key", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "delete_key", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "delete_key", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "org_id", Type: DB_BigInt, Nullable: false}, {Name: "org_id", Type: DB_BigInt, Nullable: false},
{Name: "user_id", Type: DB_BigInt, Nullable: false}, {Name: "user_id", Type: DB_BigInt, Nullable: false},
{Name: "external", Type: DB_Bool, Nullable: false}, {Name: "external", Type: DB_Bool, Nullable: false},
...@@ -54,4 +54,12 @@ func addDashboardSnapshotMigrations(mg *Migrator) { ...@@ -54,4 +54,12 @@ func addDashboardSnapshotMigrations(mg *Migrator) {
Sqlite("SELECT 0 WHERE 0;"). Sqlite("SELECT 0 WHERE 0;").
Postgres("SELECT 0;"). Postgres("SELECT 0;").
Mysql("ALTER TABLE dashboard_snapshot MODIFY dashboard MEDIUMTEXT;")) Mysql("ALTER TABLE dashboard_snapshot MODIFY dashboard MEDIUMTEXT;"))
mg.AddMigration("Update dashboard_snapshot table charset", NewTableCharsetMigration("dashboard_snapshot", []*Column{
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "key", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "delete_key", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "external_url", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "dashboard", Type: DB_MediumText, Nullable: false},
}))
} }
...@@ -10,7 +10,7 @@ func addDataSourceMigration(mg *Migrator) { ...@@ -10,7 +10,7 @@ func addDataSourceMigration(mg *Migrator) {
{Name: "account_id", Type: DB_BigInt, Nullable: false}, {Name: "account_id", Type: DB_BigInt, Nullable: false},
{Name: "version", Type: DB_Int, Nullable: false}, {Name: "version", Type: DB_Int, Nullable: false},
{Name: "type", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "type", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "name", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "access", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "access", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "url", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "url", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "password", Type: DB_NVarchar, Length: 255, Nullable: true}, {Name: "password", Type: DB_NVarchar, Length: 255, Nullable: true},
...@@ -49,7 +49,7 @@ func addDataSourceMigration(mg *Migrator) { ...@@ -49,7 +49,7 @@ func addDataSourceMigration(mg *Migrator) {
{Name: "org_id", Type: DB_BigInt, Nullable: false}, {Name: "org_id", Type: DB_BigInt, Nullable: false},
{Name: "version", Type: DB_Int, Nullable: false}, {Name: "version", Type: DB_Int, Nullable: false},
{Name: "type", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "type", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "name", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "access", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "access", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "url", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "url", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "password", Type: DB_NVarchar, Length: 255, Nullable: true}, {Name: "password", Type: DB_NVarchar, Length: 255, Nullable: true},
...@@ -106,4 +106,18 @@ func addDataSourceMigration(mg *Migrator) { ...@@ -106,4 +106,18 @@ func addDataSourceMigration(mg *Migrator) {
mg.AddMigration("Add secure json data column", NewAddColumnMigration(tableV2, &Column{ mg.AddMigration("Add secure json data column", NewAddColumnMigration(tableV2, &Column{
Name: "secure_json_data", Type: DB_Text, Nullable: true, Name: "secure_json_data", Type: DB_Text, Nullable: true,
})) }))
mg.AddMigration("Update data_source table charset", NewTableCharsetMigration(tableV2.Name, []*Column{
{Name: "type", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "name", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "access", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "url", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "password", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "user", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "database", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "basic_auth_user", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "basic_auth_password", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "json_data", Type: DB_Text, Nullable: true},
{Name: "secure_json_data", Type: DB_Text, Nullable: true},
}))
} }
...@@ -8,7 +8,7 @@ func addOrgMigrations(mg *Migrator) { ...@@ -8,7 +8,7 @@ func addOrgMigrations(mg *Migrator) {
Columns: []*Column{ Columns: []*Column{
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "version", Type: DB_Int, Nullable: false}, {Name: "version", Type: DB_Int, Nullable: false},
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "name", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "address1", Type: DB_NVarchar, Length: 255, Nullable: true}, {Name: "address1", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "address2", Type: DB_NVarchar, Length: 255, Nullable: true}, {Name: "address2", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "city", Type: DB_NVarchar, Length: 255, Nullable: true}, {Name: "city", Type: DB_NVarchar, Length: 255, Nullable: true},
...@@ -68,4 +68,19 @@ func addOrgMigrations(mg *Migrator) { ...@@ -68,4 +68,19 @@ func addOrgMigrations(mg *Migrator) {
mg.AddMigration("Drop old table account", NewDropTableMigration("account")) mg.AddMigration("Drop old table account", NewDropTableMigration("account"))
mg.AddMigration("Drop old table account_user", NewDropTableMigration("account_user")) mg.AddMigration("Drop old table account_user", NewDropTableMigration("account_user"))
mg.AddMigration("Update org table charset", NewTableCharsetMigration("org", []*Column{
{Name: "name", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "address1", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "address2", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "city", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "state", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "zip_code", Type: DB_NVarchar, Length: 50, Nullable: true},
{Name: "country", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "billing_email", Type: DB_NVarchar, Length: 255, Nullable: true},
}))
mg.AddMigration("Update org_user table charset", NewTableCharsetMigration("org_user", []*Column{
{Name: "role", Type: DB_NVarchar, Length: 20},
}))
} }
...@@ -32,4 +32,15 @@ func addPlaylistMigrations(mg *Migrator) { ...@@ -32,4 +32,15 @@ func addPlaylistMigrations(mg *Migrator) {
} }
mg.AddMigration("create playlist item table v2", NewAddTableMigration(playlistItemV2)) mg.AddMigration("create playlist item table v2", NewAddTableMigration(playlistItemV2))
mg.AddMigration("Update playlist table charset", NewTableCharsetMigration("playlist", []*Column{
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "interval", Type: DB_NVarchar, Length: 255, Nullable: false},
}))
mg.AddMigration("Update playlist_item table charset", NewTableCharsetMigration("playlist_item", []*Column{
{Name: "type", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "value", Type: DB_Text, Nullable: false},
{Name: "title", Type: DB_Text, Nullable: false},
}))
} }
...@@ -9,7 +9,7 @@ func addAppSettingsMigration(mg *Migrator) { ...@@ -9,7 +9,7 @@ func addAppSettingsMigration(mg *Migrator) {
Columns: []*Column{ Columns: []*Column{
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "org_id", Type: DB_BigInt, Nullable: true}, {Name: "org_id", Type: DB_BigInt, Nullable: true},
{Name: "plugin_id", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "plugin_id", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "enabled", Type: DB_Bool, Nullable: false}, {Name: "enabled", Type: DB_Bool, Nullable: false},
{Name: "pinned", Type: DB_Bool, Nullable: false}, {Name: "pinned", Type: DB_Bool, Nullable: false},
{Name: "json_data", Type: DB_Text, Nullable: true}, {Name: "json_data", Type: DB_Text, Nullable: true},
...@@ -32,4 +32,10 @@ func addAppSettingsMigration(mg *Migrator) { ...@@ -32,4 +32,10 @@ func addAppSettingsMigration(mg *Migrator) {
Name: "plugin_version", Type: DB_NVarchar, Nullable: true, Length: 50, Name: "plugin_version", Type: DB_NVarchar, Nullable: true, Length: 50,
})) }))
mg.AddMigration("Update plugin_setting table charset", NewTableCharsetMigration("plugin_setting", []*Column{
{Name: "plugin_id", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "json_data", Type: DB_Text, Nullable: true},
{Name: "secure_json_data", Type: DB_Text, Nullable: true},
{Name: "plugin_version", Type: DB_NVarchar, Nullable: true, Length: 50},
}))
} }
...@@ -29,4 +29,9 @@ func addPreferencesMigrations(mg *Migrator) { ...@@ -29,4 +29,9 @@ func addPreferencesMigrations(mg *Migrator) {
// create table // create table
mg.AddMigration("create preferences table v3", NewAddTableMigration(preferencesV2)) mg.AddMigration("create preferences table v3", NewAddTableMigration(preferencesV2))
mg.AddMigration("Update preferences table charset", NewTableCharsetMigration("preferences", []*Column{
{Name: "timezone", Type: DB_NVarchar, Length: 50, Nullable: false},
{Name: "theme", Type: DB_NVarchar, Length: 20, Nullable: false},
}))
} }
...@@ -12,7 +12,7 @@ func addQuotaMigration(mg *Migrator) { ...@@ -12,7 +12,7 @@ func addQuotaMigration(mg *Migrator) {
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "org_id", Type: DB_BigInt, Nullable: true}, {Name: "org_id", Type: DB_BigInt, Nullable: true},
{Name: "user_id", Type: DB_BigInt, Nullable: true}, {Name: "user_id", Type: DB_BigInt, Nullable: true},
{Name: "target", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "target", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "limit", Type: DB_BigInt, Nullable: false}, {Name: "limit", Type: DB_BigInt, Nullable: false},
{Name: "created", Type: DB_DateTime, Nullable: false}, {Name: "created", Type: DB_DateTime, Nullable: false},
{Name: "updated", Type: DB_DateTime, Nullable: false}, {Name: "updated", Type: DB_DateTime, Nullable: false},
...@@ -25,4 +25,8 @@ func addQuotaMigration(mg *Migrator) { ...@@ -25,4 +25,8 @@ func addQuotaMigration(mg *Migrator) {
//------- indexes ------------------ //------- indexes ------------------
addTableIndicesMigrations(mg, "v1", quotaV1) addTableIndicesMigrations(mg, "v1", quotaV1)
mg.AddMigration("Update quota table charset", NewTableCharsetMigration("quota", []*Column{
{Name: "target", Type: DB_NVarchar, Length: 190, Nullable: false},
}))
} }
...@@ -35,4 +35,13 @@ func addTempUserMigrations(mg *Migrator) { ...@@ -35,4 +35,13 @@ func addTempUserMigrations(mg *Migrator) {
// create table // create table
mg.AddMigration("create temp user table v1-7", NewAddTableMigration(tempUserV1)) mg.AddMigration("create temp user table v1-7", NewAddTableMigration(tempUserV1))
addTableIndicesMigrations(mg, "v1-7", tempUserV1) addTableIndicesMigrations(mg, "v1-7", tempUserV1)
mg.AddMigration("Update temp_user table charset", NewTableCharsetMigration("temp_user", []*Column{
{Name: "email", Type: DB_NVarchar, Length: 255},
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "role", Type: DB_NVarchar, Length: 20, Nullable: true},
{Name: "code", Type: DB_NVarchar, Length: 255},
{Name: "status", Type: DB_Varchar, Length: 20},
{Name: "remote_addr", Type: DB_Varchar, Length: 255, Nullable: true},
}))
} }
...@@ -8,8 +8,8 @@ func addUserMigrations(mg *Migrator) { ...@@ -8,8 +8,8 @@ func addUserMigrations(mg *Migrator) {
Columns: []*Column{ Columns: []*Column{
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "version", Type: DB_Int, Nullable: false}, {Name: "version", Type: DB_Int, Nullable: false},
{Name: "login", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "login", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "email", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "email", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: true}, {Name: "name", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "password", Type: DB_NVarchar, Length: 255, Nullable: true}, {Name: "password", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "salt", Type: DB_NVarchar, Length: 50, Nullable: true}, {Name: "salt", Type: DB_NVarchar, Length: 50, Nullable: true},
...@@ -47,8 +47,8 @@ func addUserMigrations(mg *Migrator) { ...@@ -47,8 +47,8 @@ func addUserMigrations(mg *Migrator) {
Columns: []*Column{ Columns: []*Column{
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "version", Type: DB_Int, Nullable: false}, {Name: "version", Type: DB_Int, Nullable: false},
{Name: "login", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "login", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "email", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "email", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: true}, {Name: "name", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "password", Type: DB_NVarchar, Length: 255, Nullable: true}, {Name: "password", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "salt", Type: DB_NVarchar, Length: 50, Nullable: true}, {Name: "salt", Type: DB_NVarchar, Length: 50, Nullable: true},
...@@ -92,4 +92,15 @@ func addUserMigrations(mg *Migrator) { ...@@ -92,4 +92,15 @@ func addUserMigrations(mg *Migrator) {
mg.AddMigration("Add column help_flags1 to user table", NewAddColumnMigration(userV2, &Column{ mg.AddMigration("Add column help_flags1 to user table", NewAddColumnMigration(userV2, &Column{
Name: "help_flags1", Type: DB_BigInt, Nullable: false, Default: "0", Name: "help_flags1", Type: DB_BigInt, Nullable: false, Default: "0",
})) }))
mg.AddMigration("Update user table charset", NewTableCharsetMigration("user", []*Column{
{Name: "login", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "email", Type: DB_NVarchar, Length: 190, Nullable: false},
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "password", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "salt", Type: DB_NVarchar, Length: 50, Nullable: true},
{Name: "rands", Type: DB_NVarchar, Length: 50, Nullable: true},
{Name: "company", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "theme", Type: DB_NVarchar, Length: 255, Nullable: true},
}))
} }
...@@ -29,6 +29,7 @@ type Dialect interface { ...@@ -29,6 +29,7 @@ type Dialect interface {
TableCheckSql(tableName string) (string, []interface{}) TableCheckSql(tableName string) (string, []interface{})
RenameTable(oldName string, newName string) string RenameTable(oldName string, newName string) string
UpdateTableSql(tableName string, columns []*Column) string
} }
func NewDialect(name string) Dialect { func NewDialect(name string) Dialect {
...@@ -102,7 +103,7 @@ func (b *BaseDialect) CreateTableSql(table *Table) string { ...@@ -102,7 +103,7 @@ func (b *BaseDialect) CreateTableSql(table *Table) string {
sql = sql[:len(sql)-2] + ")" sql = sql[:len(sql)-2] + ")"
if b.dialect.SupportEngine() { if b.dialect.SupportEngine() {
sql += " ENGINE=InnoDB DEFAULT CHARSET UTF8 " sql += " ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci"
} }
sql += ";" sql += ";"
...@@ -160,3 +161,7 @@ func (db *BaseDialect) DropIndexSql(tableName string, index *Index) string { ...@@ -160,3 +161,7 @@ func (db *BaseDialect) DropIndexSql(tableName string, index *Index) string {
name = index.XName(tableName) name = index.XName(tableName)
return fmt.Sprintf("DROP INDEX %v ON %s", quote(name), quote(tableName)) return fmt.Sprintf("DROP INDEX %v ON %s", quote(name), quote(tableName))
} }
func (db *BaseDialect) UpdateTableSql(tableName string, columns []*Column) string {
return "-- NOT REQUIRED"
}
...@@ -200,3 +200,17 @@ func (m *CopyTableDataMigration) IfTableExists(tableName string) *CopyTableDataM ...@@ -200,3 +200,17 @@ func (m *CopyTableDataMigration) IfTableExists(tableName string) *CopyTableDataM
func (m *CopyTableDataMigration) Sql(d Dialect) string { func (m *CopyTableDataMigration) Sql(d Dialect) string {
return d.CopyTableData(m.sourceTable, m.targetTable, m.sourceCols, m.targetCols) return d.CopyTableData(m.sourceTable, m.targetTable, m.sourceCols, m.targetCols)
} }
type TableCharsetMigration struct {
MigrationBase
tableName string
columns []*Column
}
func NewTableCharsetMigration(tableName string, columns []*Column) *TableCharsetMigration {
return &TableCharsetMigration{tableName: tableName, columns: columns}
}
func (m *TableCharsetMigration) Sql(d Dialect) string {
return d.UpdateTableSql(m.tableName, m.columns)
}
package migrator package migrator
import "strconv" import (
"strconv"
"strings"
)
type Mysql struct { type Mysql struct {
BaseDialect BaseDialect
...@@ -76,6 +79,12 @@ func (db *Mysql) SqlType(c *Column) string { ...@@ -76,6 +79,12 @@ func (db *Mysql) SqlType(c *Column) string {
} else if hasLen1 { } else if hasLen1 {
res += "(" + strconv.Itoa(c.Length) + ")" res += "(" + strconv.Itoa(c.Length) + ")"
} }
switch c.Type {
case DB_Char, DB_Varchar, DB_NVarchar, DB_TinyText, DB_Text, DB_MediumText, DB_LongText:
res += " CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
}
return res return res
} }
...@@ -84,3 +93,15 @@ func (db *Mysql) TableCheckSql(tableName string) (string, []interface{}) { ...@@ -84,3 +93,15 @@ func (db *Mysql) TableCheckSql(tableName string) (string, []interface{}) {
sql := "SELECT `TABLE_NAME` from `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? and `TABLE_NAME`=?" sql := "SELECT `TABLE_NAME` from `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? and `TABLE_NAME`=?"
return sql, args return sql, args
} }
func (db *Mysql) UpdateTableSql(tableName string, columns []*Column) string {
var statements = []string{}
statements = append(statements, "DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci")
for _, col := range columns {
statements = append(statements, "MODIFY "+col.StringNoPk(db))
}
return "ALTER TABLE " + db.Quote(tableName) + " " + strings.Join(statements, ", ") + ";"
}
...@@ -3,6 +3,7 @@ package migrator ...@@ -3,6 +3,7 @@ package migrator
import ( import (
"fmt" "fmt"
"strconv" "strconv"
"strings"
) )
type Postgres struct { type Postgres struct {
...@@ -112,3 +113,13 @@ func (db *Postgres) DropIndexSql(tableName string, index *Index) string { ...@@ -112,3 +113,13 @@ func (db *Postgres) DropIndexSql(tableName string, index *Index) string {
idxName := index.XName(tableName) idxName := index.XName(tableName)
return fmt.Sprintf("DROP INDEX %v", quote(idxName)) return fmt.Sprintf("DROP INDEX %v", quote(idxName))
} }
func (db *Postgres) UpdateTableSql(tableName string, columns []*Column) string {
var statements = []string{}
for _, col := range columns {
statements = append(statements, "ALTER "+db.QuoteStr()+col.Name+db.QuoteStr()+" TYPE "+db.SqlType(col))
}
return "ALTER TABLE " + db.Quote(tableName) + " " + strings.Join(statements, ", ") + ";"
}
...@@ -29,7 +29,6 @@ type DatabaseConfig struct { ...@@ -29,7 +29,6 @@ type DatabaseConfig struct {
ClientKeyPath string ClientKeyPath string
ClientCertPath string ClientCertPath string
ServerCertName string ServerCertName string
MaxConn int
MaxOpenConn int MaxOpenConn int
MaxIdleConn int MaxIdleConn int
} }
...@@ -115,7 +114,7 @@ func getEngine() (*xorm.Engine, error) { ...@@ -115,7 +114,7 @@ func getEngine() (*xorm.Engine, error) {
protocol = "unix" protocol = "unix"
} }
cnnstr = fmt.Sprintf("%s:%s@%s(%s)/%s?charset=utf8", cnnstr = fmt.Sprintf("%s:%s@%s(%s)/%s?charset=utf8mb4",
DbCfg.User, DbCfg.Pwd, protocol, DbCfg.Host, DbCfg.Name) DbCfg.User, DbCfg.Pwd, protocol, DbCfg.Host, DbCfg.Name)
if DbCfg.SslMode == "true" || DbCfg.SslMode == "skip-verify" { if DbCfg.SslMode == "true" || DbCfg.SslMode == "skip-verify" {
...@@ -157,7 +156,6 @@ func getEngine() (*xorm.Engine, error) { ...@@ -157,7 +156,6 @@ func getEngine() (*xorm.Engine, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} else { } else {
engine.SetMaxConns(DbCfg.MaxConn)
engine.SetMaxOpenConns(DbCfg.MaxOpenConn) engine.SetMaxOpenConns(DbCfg.MaxOpenConn)
engine.SetMaxIdleConns(DbCfg.MaxIdleConn) engine.SetMaxIdleConns(DbCfg.MaxIdleConn)
// engine.SetLogger(NewXormLogger(log.LvlInfo, log.New("sqlstore.xorm"))) // engine.SetLogger(NewXormLogger(log.LvlInfo, log.New("sqlstore.xorm")))
...@@ -191,7 +189,6 @@ func LoadConfig() { ...@@ -191,7 +189,6 @@ func LoadConfig() {
DbCfg.Host = sec.Key("host").String() DbCfg.Host = sec.Key("host").String()
DbCfg.Name = sec.Key("name").String() DbCfg.Name = sec.Key("name").String()
DbCfg.User = sec.Key("user").String() DbCfg.User = sec.Key("user").String()
DbCfg.MaxConn = sec.Key("max_conn").MustInt(0)
DbCfg.MaxOpenConn = sec.Key("max_open_conn").MustInt(0) DbCfg.MaxOpenConn = sec.Key("max_open_conn").MustInt(0)
DbCfg.MaxIdleConn = sec.Key("max_idle_conn").MustInt(0) DbCfg.MaxIdleConn = sec.Key("max_idle_conn").MustInt(0)
if len(DbCfg.Pwd) == 0 { if len(DbCfg.Pwd) == 0 {
......
...@@ -12,7 +12,7 @@ type TestDB struct { ...@@ -12,7 +12,7 @@ type TestDB struct {
} }
var TestDB_Sqlite3 = TestDB{DriverName: "sqlite3", ConnStr: ":memory:?_loc=Local"} var TestDB_Sqlite3 = TestDB{DriverName: "sqlite3", ConnStr: ":memory:?_loc=Local"}
var TestDB_Mysql = TestDB{DriverName: "mysql", ConnStr: "grafana:password@tcp(localhost:3306)/grafana_tests?charset=utf8"} var TestDB_Mysql = TestDB{DriverName: "mysql", ConnStr: "grafana:password@tcp(localhost:3306)/grafana_tests?charset=utf8mb4"}
var TestDB_Postgres = TestDB{DriverName: "postgres", ConnStr: "user=grafanatest password=grafanatest host=localhost port=5432 dbname=grafanatest sslmode=disable"} var TestDB_Postgres = TestDB{DriverName: "postgres", ConnStr: "user=grafanatest password=grafanatest host=localhost port=5432 dbname=grafanatest sslmode=disable"}
func CleanDB(x *xorm.Engine) { func CleanDB(x *xorm.Engine) {
......
...@@ -96,6 +96,7 @@ var ( ...@@ -96,6 +96,7 @@ var (
LoginHint string LoginHint string
DefaultTheme string DefaultTheme string
DisableLoginForm bool DisableLoginForm bool
DisableSignoutMenu bool
// Http auth // Http auth
AdminUser string AdminUser string
...@@ -528,6 +529,7 @@ func NewConfigContext(args *CommandLineArgs) error { ...@@ -528,6 +529,7 @@ func NewConfigContext(args *CommandLineArgs) error {
// auth // auth
auth := Cfg.Section("auth") auth := Cfg.Section("auth")
DisableLoginForm = auth.Key("disable_login_form").MustBool(false) DisableLoginForm = auth.Key("disable_login_form").MustBool(false)
DisableSignoutMenu = auth.Key("disable_signout_menu").MustBool(false)
// anonymous access // anonymous access
AnonymousEnabled = Cfg.Section("auth.anonymous").Key("enabled").MustBool(false) AnonymousEnabled = Cfg.Section("auth.anonymous").Key("enabled").MustBool(false)
......
...@@ -23,7 +23,7 @@ export class SideMenuCtrl { ...@@ -23,7 +23,7 @@ export class SideMenuCtrl {
this.isSignedIn = contextSrv.isSignedIn; this.isSignedIn = contextSrv.isSignedIn;
this.user = contextSrv.user; this.user = contextSrv.user;
this.appSubUrl = config.appSubUrl; this.appSubUrl = config.appSubUrl;
this.showSignout = this.contextSrv.isSignedIn && !config['authProxyEnabled']; this.showSignout = this.contextSrv.isSignedIn && !config['disableSignoutMenu'];
this.maxShownOrgs = 10; this.maxShownOrgs = 10;
this.mainLinks = config.bootData.mainNavLinks; this.mainLinks = config.bootData.mainNavLinks;
......
...@@ -156,11 +156,21 @@ module.directive('grafanaPanel', function($rootScope, $document) { ...@@ -156,11 +156,21 @@ module.directive('grafanaPanel', function($rootScope, $document) {
content: function() { content: function() {
return ctrl.getInfoContent({mode: 'tooltip'}); return ctrl.getInfoContent({mode: 'tooltip'});
}, },
position: 'top center',
classes: ctrl.error ? 'drop-error' : 'drop-help', classes: ctrl.error ? 'drop-error' : 'drop-help',
openOn: 'hover', openOn: 'hover',
hoverOpenDelay: 100, hoverOpenDelay: 100,
constrainToScrollParent: false, remove: true,
tetherOptions: {
attachment: 'bottom left',
targetAttachment: 'top left',
constraints: [
{
to: 'window',
attachment: 'together',
pin: true
}
],
}
}); });
} }
} }
...@@ -185,6 +195,10 @@ module.directive('grafanaPanel', function($rootScope, $document) { ...@@ -185,6 +195,10 @@ module.directive('grafanaPanel', function($rootScope, $document) {
if (ctrl.skippedLastRefresh) { if (ctrl.skippedLastRefresh) {
ctrl.refresh(); ctrl.refresh();
} }
if (infoDrop) {
infoDrop.position();
}
}; };
$document.on('scroll', refreshOnScroll); $document.on('scroll', refreshOnScroll);
......
...@@ -50,6 +50,7 @@ function (angular, _, queryDef) { ...@@ -50,6 +50,7 @@ function (angular, _, queryDef) {
switch($scope.agg.type) { switch($scope.agg.type) {
case 'date_histogram': case 'date_histogram':
case 'histogram':
case 'terms': { case 'terms': {
delete $scope.agg.query; delete $scope.agg.query;
$scope.agg.field = 'select field'; $scope.agg.field = 'select field';
...@@ -132,6 +133,16 @@ function (angular, _, queryDef) { ...@@ -132,6 +133,16 @@ function (angular, _, queryDef) {
} }
break; break;
} }
case 'histogram': {
settings.interval = settings.interval || 1000;
settings.min_doc_count = _.defaultTo(settings.min_doc_count, 1);
settingsLinkText = 'Interval: ' + settings.interval;
if (settings.min_doc_count > 0) {
settingsLinkText += ', Min Doc Count: ' + settings.min_doc_count;
}
break;
}
case 'geohash_grid': { case 'geohash_grid': {
// limit precision to 7 // limit precision to 7
settings.precision = Math.max(Math.min(settings.precision, 7), 1); settings.precision = Math.max(Math.min(settings.precision, 7), 1);
......
...@@ -52,6 +52,17 @@ ...@@ -52,6 +52,17 @@
</div> </div>
</div> </div>
<div ng-if="agg.type === 'histogram'">
<div class="gf-form offset-width-7">
<label class="gf-form-label width-10">Interval</label>
<input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.interval" ng-blur="onChangeInternal()">
</div>
<div class="gf-form offset-width-7">
<label class="gf-form-label width-10">Min Doc Count</label>
<input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.min_doc_count" ng-blur="onChangeInternal()">
</div>
</div>
<div ng-if="agg.type === 'terms'"> <div ng-if="agg.type === 'terms'">
<div class="gf-form offset-width-7"> <div class="gf-form offset-width-7">
<label class="gf-form-label width-10">Order</label> <label class="gf-form-label width-10">Order</label>
......
...@@ -79,6 +79,19 @@ function (queryDef) { ...@@ -79,6 +79,19 @@ function (queryDef) {
return esAgg; return esAgg;
}; };
ElasticQueryBuilder.prototype.getHistogramAgg = function(aggDef) {
var esAgg = {};
var settings = aggDef.settings || {};
esAgg.interval = settings.interval;
esAgg.field = aggDef.field;
esAgg.min_doc_count = settings.min_doc_count || 0;
if (settings.missing) {
esAgg.missing = settings.missing;
}
return esAgg;
};
ElasticQueryBuilder.prototype.getFiltersAgg = function(aggDef) { ElasticQueryBuilder.prototype.getFiltersAgg = function(aggDef) {
var filterObj = {}; var filterObj = {};
for (var i = 0; i < aggDef.settings.filters.length; i++) { for (var i = 0; i < aggDef.settings.filters.length; i++) {
...@@ -192,6 +205,10 @@ function (queryDef) { ...@@ -192,6 +205,10 @@ function (queryDef) {
esAgg["date_histogram"] = this.getDateHistogramAgg(aggDef); esAgg["date_histogram"] = this.getDateHistogramAgg(aggDef);
break; break;
} }
case 'histogram': {
esAgg["histogram"] = this.getHistogramAgg(aggDef);
break;
}
case 'filters': { case 'filters': {
esAgg["filters"] = {filters: this.getFiltersAgg(aggDef)}; esAgg["filters"] = {filters: this.getFiltersAgg(aggDef)};
break; break;
......
...@@ -24,6 +24,7 @@ function (_) { ...@@ -24,6 +24,7 @@ function (_) {
{text: "Filters", value: 'filters' }, {text: "Filters", value: 'filters' },
{text: "Geo Hash Grid", value: 'geohash_grid', requiresField: true}, {text: "Geo Hash Grid", value: 'geohash_grid', requiresField: true},
{text: "Date Histogram", value: 'date_histogram', requiresField: true}, {text: "Date Histogram", value: 'date_histogram', requiresField: true},
{text: "Histogram", value: 'histogram', requiresField: true},
], ],
orderByOptions: [ orderByOptions: [
......
...@@ -361,6 +361,39 @@ describe('ElasticResponse', function() { ...@@ -361,6 +361,39 @@ describe('ElasticResponse', function() {
}); });
}); });
describe('histogram response', function() {
var result;
beforeEach(function() {
targets = [{
refId: 'A',
metrics: [{type: 'count', id: '1'}],
bucketAggs: [{type: 'histogram', field: 'bytes', id: '3'}],
}];
response = {
responses: [{
aggregations: {
"3": {
buckets: [
{doc_count: 1, key: 1000},
{doc_count: 3, key: 2000},
{doc_count: 2, key: 1000},
]
}
}
}]
};
result = new ElasticResponse(targets, response).getTimeSeries();
});
it('should return docs with byte and count', function() {
expect(result.data[0].datapoints.length).to.be(3);
expect(result.data[0].datapoints[0].Count).to.be(1);
expect(result.data[0].datapoints[0].bytes).to.be(1000);
});
});
describe('with two filters agg', function() { describe('with two filters agg', function() {
var result; var result;
......
...@@ -249,6 +249,23 @@ describe('ElasticQueryBuilder', function() { ...@@ -249,6 +249,23 @@ describe('ElasticQueryBuilder', function() {
expect(firstLevel.aggs["2"].derivative.buckets_path).to.be("3"); expect(firstLevel.aggs["2"].derivative.buckets_path).to.be("3");
}); });
it('with histogram', function() {
var query = builder.build({
metrics: [
{id: '1', type: 'count' },
],
bucketAggs: [
{type: 'histogram', field: 'bytes', id: '3', settings: {interval: 10, min_doc_count: 2, missing: 5}}
],
});
var firstLevel = query.aggs["3"];
expect(firstLevel.histogram.field).to.be('bytes');
expect(firstLevel.histogram.interval).to.be(10);
expect(firstLevel.histogram.min_doc_count).to.be(2);
expect(firstLevel.histogram.missing).to.be(5);
});
it('with adhoc filters', function() { it('with adhoc filters', function() {
var query = builder.build({ var query = builder.build({
metrics: [{type: 'Count', id: '0'}], metrics: [{type: 'Count', id: '0'}],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment