diff options
author | George Goldberg <george@gberg.me> | 2017-07-13 21:00:59 +0100 |
---|---|---|
committer | Christopher Speller <crspeller@gmail.com> | 2017-07-13 13:00:59 -0700 |
commit | a1f17c1f8482f3c38e17cabe2365c76a1a2f32d4 (patch) | |
tree | 8681a3840d9881dad385628f74310e1576a188c4 | |
parent | 4eed88e0c2eebbaf04489526dfaeab7cfb77ee98 (diff) | |
download | chat-a1f17c1f8482f3c38e17cabe2365c76a1a2f32d4.tar.gz chat-a1f17c1f8482f3c38e17cabe2365c76a1a2f32d4.tar.bz2 chat-a1f17c1f8482f3c38e17cabe2365c76a1a2f32d4.zip |
PLT-6474 Basic Elasticsearch System Console Page (#6825)
* PLT-6474: Basic System Console Elasticsearch Config.
* Fix review comments.
* More review fixes.
* Review comments.
-rw-r--r-- | model/config.go | 2 | ||||
-rw-r--r-- | utils/config.go | 11 | ||||
-rw-r--r-- | webapp/actions/admin_actions.jsx | 13 | ||||
-rw-r--r-- | webapp/components/admin_console/admin_sidebar.jsx | 16 | ||||
-rw-r--r-- | webapp/components/admin_console/elasticsearch_settings.jsx | 280 | ||||
-rwxr-xr-x | webapp/i18n/en.json | 21 | ||||
-rw-r--r-- | webapp/routes/route_admin_console.jsx | 5 | ||||
-rw-r--r-- | webapp/yarn.lock | 2 |
8 files changed, 336 insertions, 14 deletions
diff --git a/model/config.go b/model/config.go index 3e98aa8f6..7bf891462 100644 --- a/model/config.go +++ b/model/config.go @@ -1662,8 +1662,6 @@ func (o *Config) Sanitize() { o.SqlSettings.DataSourceSearchReplicas[i] = FAKE_SETTING } - *o.ElasticSearchSettings.ConnectionUrl = FAKE_SETTING - *o.ElasticSearchSettings.Username = FAKE_SETTING *o.ElasticSearchSettings.Password = FAKE_SETTING } diff --git a/utils/config.go b/utils/config.go index f8ceccc2c..478e3ba99 100644 --- a/utils/config.go +++ b/utils/config.go @@ -536,11 +536,6 @@ func getClientConfig(c *model.Config) map[string]string { props["PasswordRequireSymbol"] = strconv.FormatBool(*c.PasswordSettings.Symbol) } - if *License.Features.Elasticsearch { - props["ElasticSearchEnableIndexing"] = strconv.FormatBool(*c.ElasticSearchSettings.EnableIndexing) - props["ElasticSearchEnableSearching"] = strconv.FormatBool(*c.ElasticSearchSettings.EnableSearching) - } - if *License.Features.Announcement { props["EnableBanner"] = strconv.FormatBool(*c.AnnouncementSettings.EnableBanner) props["BannerText"] = *c.AnnouncementSettings.BannerText @@ -616,12 +611,6 @@ func Desanitize(cfg *model.Config) { cfg.SqlSettings.AtRestEncryptKey = Cfg.SqlSettings.AtRestEncryptKey } - if *cfg.ElasticSearchSettings.ConnectionUrl == model.FAKE_SETTING { - *cfg.ElasticSearchSettings.ConnectionUrl = *Cfg.ElasticSearchSettings.ConnectionUrl - } - if *cfg.ElasticSearchSettings.Username == model.FAKE_SETTING { - *cfg.ElasticSearchSettings.Username = *Cfg.ElasticSearchSettings.Username - } if *cfg.ElasticSearchSettings.Password == model.FAKE_SETTING { *cfg.ElasticSearchSettings.Password = *Cfg.ElasticSearchSettings.Password } diff --git a/webapp/actions/admin_actions.jsx b/webapp/actions/admin_actions.jsx index fdaeb8732..ac02ac058 100644 --- a/webapp/actions/admin_actions.jsx +++ b/webapp/actions/admin_actions.jsx @@ -383,3 +383,16 @@ export function getPostsPerDayAnalytics(teamId) { export function getUsersPerDayAnalytics(teamId) { AdminActions.getUsersPerDayAnalytics(teamId)(dispatch, getState); } + +export function elasticsearchTest(config, success, error) { + AdminActions.testElasticsearch(config)(dispatch, getState).then( + (data) => { + if (data && success) { + success(data); + } else if (data == null && error) { + const serverError = getState().requests.admin.testElasticsearch.error; + error({id: serverError.server_error_id, ...serverError}); + } + } + ); +} diff --git a/webapp/components/admin_console/admin_sidebar.jsx b/webapp/components/admin_console/admin_sidebar.jsx index 1dbbff2f2..9b27ab81e 100644 --- a/webapp/components/admin_console/admin_sidebar.jsx +++ b/webapp/components/admin_console/admin_sidebar.jsx @@ -258,6 +258,21 @@ export default class AdminSidebar extends React.Component { /> ); + let elasticSearchSettings = null; + if (window.mm_license.IsLicensed === 'true') { + elasticSearchSettings = ( + <AdminSidebarSection + name='elasticsearch' + title={ + <FormattedMessage + id='admin.sidebar.elasticsearch' + defaultMessage='Elasticsearch' + /> + } + /> + ); + } + return ( <div className='admin-sidebar'> <AdminSidebarHeader/> @@ -618,6 +633,7 @@ export default class AdminSidebar extends React.Component { /> } /> + {elasticSearchSettings} <AdminSidebarSection name='developer' title={ diff --git a/webapp/components/admin_console/elasticsearch_settings.jsx b/webapp/components/admin_console/elasticsearch_settings.jsx new file mode 100644 index 000000000..23ba14b25 --- /dev/null +++ b/webapp/components/admin_console/elasticsearch_settings.jsx @@ -0,0 +1,280 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import React from 'react'; + +import * as Utils from 'utils/utils.jsx'; + +import AdminSettings from './admin_settings.jsx'; +import {elasticsearchTest} from 'actions/admin_actions.jsx'; +import BooleanSetting from './boolean_setting.jsx'; +import {FormattedMessage} from 'react-intl'; +import SettingsGroup from './settings_group.jsx'; +import TextSetting from './text_setting.jsx'; +import RequestButton from './request_button/request_button.jsx'; + +export default class ElasticsearchSettings extends AdminSettings { + constructor(props) { + super(props); + + this.getConfigFromState = this.getConfigFromState.bind(this); + + this.doTestConfig = this.doTestConfig.bind(this); + this.handleChange = this.handleChange.bind(this); + + this.renderSettings = this.renderSettings.bind(this); + } + + getConfigFromState(config) { + config.ElasticSearchSettings.ConnectionUrl = this.state.connectionUrl; + config.ElasticSearchSettings.Username = this.state.username; + config.ElasticSearchSettings.Password = this.state.password; + config.ElasticSearchSettings.Sniff = this.state.sniff; + config.ElasticSearchSettings.EnableIndexing = this.state.enableIndexing; + config.ElasticSearchSettings.EnableSearching = this.state.enableSearching; + + return config; + } + + getStateFromConfig(config) { + return { + connectionUrl: config.ElasticSearchSettings.ConnectionUrl, + username: config.ElasticSearchSettings.Username, + password: config.ElasticSearchSettings.Password, + sniff: config.ElasticSearchSettings.Sniff, + enableIndexing: config.ElasticSearchSettings.EnableIndexing, + enableSearching: config.ElasticSearchSettings.EnableSearching, + configTested: true, + canSave: true + }; + } + + handleChange(id, value) { + if (id === 'enableIndexing') { + if (value === false) { + this.setState({ + enableSearching: false + }); + } else { + this.setState({ + canSave: false, + configTested: false + }); + } + } + + if (id === 'connectionUrl' || id === 'username' || id === 'password' || id === 'sniff') { + this.setState({ + configTested: false, + canSave: false + }); + } + + super.handleChange(id, value); + } + + canSave() { + return this.state.canSave; + } + + doTestConfig(success, error) { + const config = JSON.parse(JSON.stringify(this.props.config)); + this.getConfigFromState(config); + + elasticsearchTest( + config, + () => { + this.setState({ + configTested: true, + canSave: true + }); + success(); + }, + (err) => { + this.setState({ + configTested: false, + canSave: false + }); + error(err); + } + ); + } + + renderTitle() { + return ( + <FormattedMessage + id='admin.elasticsearch.title' + defaultMessage='Elasticsearch Settings' + /> + ); + } + + renderSettings() { + return ( + <SettingsGroup> + <div className='banner'> + <div className='banner__content'> + <FormattedMessage + id='admin.elasticsearch.noteDescription' + defaultMessage='Changing properties in this section will require a server restart before taking effect.' + /> + </div> + </div> + <BooleanSetting + id='enableIndexing' + label={ + <FormattedMessage + id='admin.elasticsearch.enableIndexingTitle' + defaultMessage='Enable Elasticsearch Indexing:' + /> + } + helpText={ + <FormattedMessage + id='admin.elasticsearch.enableIndexingDescription' + defaultMessage='When true, indexing of new posts occurs automatically. Search queries will use database search until "Enable Elasticsearch for search queries" is enabled. {documentationLink}' + values={{ + documentationLink: ( + <a + href='http://www.mattermost.com' + rel='noopener noreferrer' + target='_blank' + > + <FormattedMessage + id='admin.elasticsearch.enableIndexingDescription.documentationLinkText' + defaultMessage='Learn more about Elasticsearch in our documentation.' + /> + </a> + ) + }} + /> + } + value={this.state.enableIndexing} + onChange={this.handleChange} + /> + <TextSetting + id='connectionUrl' + label={ + <FormattedMessage + id='admin.elasticsearch.connectionUrlTitle' + defaultMessage='Server Connection Address:' + /> + } + placeholder={Utils.localizeMessage('admin.elasticsearch.connectionUrlExample', 'E.g.: "https://elasticsearch.example.org:9200"')} + helpText={ + <FormattedMessage + id='admin.elasticsearch.connectionUrlDescription' + defaultMessage='The address of the Elasticsearch server. {documentationLink}' + values={{ + documentationLink: ( + <a + href='http://www.mattermost.com' + rel='noopener noreferrer' + target='_blank' + > + <FormattedMessage + id='admin.elasticsearch.connectionUrlExample.documentationLinkText' + defaultMessage='Please see documentation with server setup instructions.' + /> + </a> + ) + }} + /> + } + value={this.state.connectionUrl} + disabled={!this.state.enableIndexing} + onChange={this.handleChange} + /> + <TextSetting + id='username' + label={ + <FormattedMessage + id='admin.elasticsearch.usernameTitle' + defaultMessage='Server Username:' + /> + } + placeholder={Utils.localizeMessage('admin.elasticsearch.usernameExample', 'E.g.: "elastic"')} + helpText={ + <FormattedMessage + id='admin.elasticsearch.usernameDescription' + defaultMessage='(Optional) The username to authenticate to the Elasticsearch server.' + /> + } + value={this.state.username} + disabled={!this.state.enableIndexing} + onChange={this.handleChange} + /> + <TextSetting + id='password' + label={ + <FormattedMessage + id='admin.elasticsearch.passwordTitle' + defaultMessage='Server Password:' + /> + } + placeholder={Utils.localizeMessage('admin.elasticsearch.password', 'E.g.: "yourpassword"')} + helpText={ + <FormattedMessage + id='admin.elasticsearch.passwordDescription' + defaultMessage='(Optional) The password to authenticate to the Elasticsearch server.' + /> + } + value={this.state.password} + disabled={!this.state.enableIndexing} + onChange={this.handleChange} + /> + <BooleanSetting + id='sniff' + label={ + <FormattedMessage + id='admin.elasticsearch.sniffTitle' + defaultMessage='Enable Cluster Sniffing:' + /> + } + helpText={ + <FormattedMessage + id='admin.elasticsearch.sniffDescription' + defaultMessage='When true, sniffing finds and connects to all data nodes in your cluster automatically.' + /> + } + value={this.state.sniff} + disabled={!this.state.enableIndexing} + onChange={this.handleChange} + /> + <RequestButton + requestAction={this.doTestConfig} + helpText={ + <FormattedMessage + id='admin.elasticsearch.testHelpText' + defaultMessage='Tests if the Mattermost server can connect to the Elasticsearch server specified. Testing the connection does not save the configuration. See log file for more detailed error messages.' + /> + } + buttonText={ + <FormattedMessage + id='admin.elasticsearch.elasticsearch_test_button' + defaultMessage='Test Connection' + /> + } + disabled={!this.state.enableIndexing} + /> + <BooleanSetting + id='enableSearching' + label={ + <FormattedMessage + id='admin.elasticsearch.enableSearchingTitle' + defaultMessage='Enable Elasticsearch for search queries:' + /> + } + helpText={ + <FormattedMessage + id='admin.elasticsearch.enableSearchingDescription' + defaultMessage='Requires a successful connection to the Elasticsearch server. When true, Elasticsearch will be used for all search queries using the latest index. Search results may be incomplete until a bulk index of the existing post database is finished. When false, database search is used.' + /> + } + value={this.state.enableSearching} + disabled={!this.state.enableIndexing || !this.state.configTested} + onChange={this.handleChange} + /> + </SettingsGroup> + ); + } +} diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json index c4c0951f4..684fe7329 100755 --- a/webapp/i18n/en.json +++ b/webapp/i18n/en.json @@ -235,6 +235,27 @@ "admin.customization.support": "Legal and Support", "admin.database.title": "Database Settings", "admin.developer.title": "Developer Settings", + "admin.elasticsearch.title": "Elasticsearch Settings", + "admin.elasticsearch.noteDescription": "Changing properties in this section will require a server restart before taking effect.", + "admin.elasticsearch.enableIndexingTitle": "Enable Elasticsearch Indexing:", + "admin.elasticsearch.enableIndexingDescription": "When true, indexing of new posts occurs automatically. Search queries will use database search until \"Enable Elasticsearch for search queries\" is enabled. {documentationLink}", + "admin.elasticsearch.enableIndexingDescription.documentationLinkText": "Learn more about Elasticsearch in our documentation.", + "admin.elasticsearch.connectionUrlTitle": "Server Connection Address:", + "admin.elasticsearch.connectionUrlDescription": "The address of the Elasticsearch server. {documentationLink}", + "admin.elasticsearch.connectionUrlExample.documentationLinkText": "Please see documentation with server setup instructions.", + "admin.elasticsearch.usernameTitle": "Server Username:", + "admin.elasticsearch.usernameDescription": "(Optional) The username to authenticate to the Elasticsearch server.", + "admin.elasticsearch.passwordTitle": "Server Password:", + "admin.elasticsearch.passwordDescription": "(Optional) The password to authenticate to the Elasticsearch server.", + "admin.elasticsearch.sniffTitle": "Enable Cluster Sniffing:", + "admin.elasticsearch.sniffDescription": "When true, sniffing finds and connects to all data nodes in your cluster automatically.", + "admin.elasticsearch.enableSearchingTitle": "Enable Elasticsearch for search queries:", + "admin.elasticsearch.enableSearchingDescription": "Requires a successful connection to the Elasticsearch server. When true, Elasticsearch will be used for all search queries using the latest index. Search results may be incomplete until a bulk index of the existing post database is finished. When false, database search is used.", + "admin.elasticsearch.connectionUrlExample": "E.g.: \"https://elasticsearch.example.org:9200\"", + "admin.elasticsearch.usernameExample": "E.g.: \"elastic\"", + "admin.elasticsearch.password": "E.g.: \"yourpassword\"", + "admin.elasticsearch.testHelpText": "Tests if the Mattermost server can connect to the Elasticsearch server specified. Testing the connection does not save the configuration. See log file for more detailed error messages.", + "admin.elasticsearch.elasticsearch_test_button": "Test Connection", "admin.email.agreeHPNS": " I understand and accept the Mattermost Hosted Push Notification Service <a href=\"https://about.mattermost.com/hpns-terms/\" target='_blank'>Terms of Service</a> and <a href=\"https://about.mattermost.com/hpns-privacy/\" target='_blank'>Privacy Policy</a>.", "admin.email.allowEmailSignInDescription": "When true, Mattermost allows users to sign in using their email and password.", "admin.email.allowEmailSignInTitle": "Enable sign-in with email: ", diff --git a/webapp/routes/route_admin_console.jsx b/webapp/routes/route_admin_console.jsx index b0b6ebf62..17e0290c2 100644 --- a/webapp/routes/route_admin_console.jsx +++ b/webapp/routes/route_admin_console.jsx @@ -45,6 +45,7 @@ import TeamAnalytics from 'components/analytics/team_analytics'; import LicenseSettings from 'components/admin_console/license_settings.jsx'; import Audits from 'components/admin_console/audits'; import Logs from 'components/admin_console/server_logs'; +import ElasticsearchSettings from 'components/admin_console/elasticsearch_settings.jsx'; export default ( <Route> @@ -200,6 +201,10 @@ export default ( component={DatabaseSettings} /> <Route + path='elasticsearch' + component={ElasticsearchSettings} + /> + <Route path='developer' component={DeveloperSettings} /> diff --git a/webapp/yarn.lock b/webapp/yarn.lock index ed9c41742..21ac7dbef 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -5047,7 +5047,7 @@ math-expression-evaluator@^1.2.14: mattermost-redux@mattermost/mattermost-redux#webapp-master: version "0.0.1" - resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/dd48556075c8be41aa5ac4a0165bbe830d496875" + resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/a850fc2696c7083a068f7852c1d4b8bce0ec72ba" dependencies: deep-equal "1.0.1" harmony-reflect "1.5.1" |