MM-49984: Displaying active search backend in system console (#22721)
This will be shown in the system console to let users know which search backend is active. Implemented via adding an extra param in the ping response. https://mattermost.atlassian.net/browse/MM-49984 ```release-note The database section in the system console now has an additional read-only section which shows the active search backend in use. This can be helpful to confirm which is the currently active search engine when there are multiple of them configured. ``` --------- Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
4bf31bd227
Коммит
56bf1b695a
@@ -190,6 +190,8 @@ func getSystemPing(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
s["CanReceiveNotifications"] = c.App.SendTestPushNotification(deviceID)
|
s["CanReceiveNotifications"] = c.App.SendTestPushNotification(deviceID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s["ActiveSearchBackend"] = c.App.ActiveSearchBackend()
|
||||||
|
|
||||||
if s[model.STATUS] != model.StatusOk {
|
if s[model.STATUS] != model.StatusOk {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -400,6 +400,7 @@ type AppIface interface {
|
|||||||
VerifyPlugin(plugin, signature io.ReadSeeker) *model.AppError
|
VerifyPlugin(plugin, signature io.ReadSeeker) *model.AppError
|
||||||
AccountMigration() einterfaces.AccountMigrationInterface
|
AccountMigration() einterfaces.AccountMigrationInterface
|
||||||
ActivateMfa(userID, token string) *model.AppError
|
ActivateMfa(userID, token string) *model.AppError
|
||||||
|
ActiveSearchBackend() string
|
||||||
AddChannelsToRetentionPolicy(policyID string, channelIDs []string) *model.AppError
|
AddChannelsToRetentionPolicy(policyID string, channelIDs []string) *model.AppError
|
||||||
AddConfigListener(listener func(*model.Config, *model.Config)) string
|
AddConfigListener(listener func(*model.Config, *model.Config)) string
|
||||||
AddDirectChannels(c request.CTX, teamID string, user *model.User) *model.AppError
|
AddDirectChannels(c request.CTX, teamID string, user *model.User) *model.AppError
|
||||||
|
|||||||
@@ -89,6 +89,23 @@ func (a *OpenTracingAppLayer) ActivateMfa(userID string, token string) *model.Ap
|
|||||||
return resultVar0
|
return resultVar0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *OpenTracingAppLayer) ActiveSearchBackend() string {
|
||||||
|
origCtx := a.ctx
|
||||||
|
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.ActiveSearchBackend")
|
||||||
|
|
||||||
|
a.ctx = newCtx
|
||||||
|
a.app.Srv().Store().SetContext(newCtx)
|
||||||
|
defer func() {
|
||||||
|
a.app.Srv().Store().SetContext(origCtx)
|
||||||
|
a.ctx = origCtx
|
||||||
|
}()
|
||||||
|
|
||||||
|
defer span.Finish()
|
||||||
|
resultVar0 := a.app.ActiveSearchBackend()
|
||||||
|
|
||||||
|
return resultVar0
|
||||||
|
}
|
||||||
|
|
||||||
func (a *OpenTracingAppLayer) AddChannelMember(c request.CTX, userID string, channel *model.Channel, opts app.ChannelMemberOpts) (*model.ChannelMember, *model.AppError) {
|
func (a *OpenTracingAppLayer) AddChannelMember(c request.CTX, userID string, channel *model.Channel, opts app.ChannelMemberOpts) (*model.ChannelMember, *model.AppError) {
|
||||||
origCtx := a.ctx
|
origCtx := a.ctx
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.AddChannelMember")
|
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.AddChannelMember")
|
||||||
|
|||||||
@@ -60,3 +60,7 @@ func (a *App) PurgeBleveIndexes() *model.AppError {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) ActiveSearchBackend() string {
|
||||||
|
return a.ch.srv.platform.SearchEngine.ActiveEngine()
|
||||||
|
}
|
||||||
|
|||||||
@@ -7795,6 +7795,10 @@
|
|||||||
"id": "ent.elasticsearch.indexer.index_batch.nothing_left_to_index.error",
|
"id": "ent.elasticsearch.indexer.index_batch.nothing_left_to_index.error",
|
||||||
"translation": "Trying to index a new batch when all the entities are completed"
|
"translation": "Trying to index a new batch when all the entities are completed"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "ent.elasticsearch.max_version.app_error",
|
||||||
|
"translation": "Elasticsearch version {{.Version}} is higher than max supported version of {{.MaxVersion}}"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "ent.elasticsearch.not_started.error",
|
"id": "ent.elasticsearch.not_started.error",
|
||||||
"translation": "Elasticsearch is not started"
|
"translation": "Elasticsearch is not started"
|
||||||
@@ -7863,10 +7867,6 @@
|
|||||||
"id": "ent.elasticsearch.search_users.unmarshall_user_failed",
|
"id": "ent.elasticsearch.search_users.unmarshall_user_failed",
|
||||||
"translation": "Failed to decode search results"
|
"translation": "Failed to decode search results"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"id": "ent.elasticsearch.start.already_started.app_error",
|
|
||||||
"translation": "Elasticsearch is already started."
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "ent.elasticsearch.start.create_bulk_processor_failed.app_error",
|
"id": "ent.elasticsearch.start.create_bulk_processor_failed.app_error",
|
||||||
"translation": "Failed to create Elasticsearch bulk processor."
|
"translation": "Failed to create Elasticsearch bulk processor."
|
||||||
|
|||||||
@@ -45,8 +45,19 @@ func (seb *Broker) GetActiveEngines() []SearchEngineInterface {
|
|||||||
if seb.ElasticsearchEngine != nil && seb.ElasticsearchEngine.IsActive() {
|
if seb.ElasticsearchEngine != nil && seb.ElasticsearchEngine.IsActive() {
|
||||||
engines = append(engines, seb.ElasticsearchEngine)
|
engines = append(engines, seb.ElasticsearchEngine)
|
||||||
}
|
}
|
||||||
if seb.BleveEngine != nil && seb.BleveEngine.IsActive() {
|
if seb.BleveEngine != nil && seb.BleveEngine.IsActive() && seb.BleveEngine.IsIndexingEnabled() {
|
||||||
engines = append(engines, seb.BleveEngine)
|
engines = append(engines, seb.BleveEngine)
|
||||||
}
|
}
|
||||||
return engines
|
return engines
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (seb *Broker) ActiveEngine() string {
|
||||||
|
activeEngines := seb.GetActiveEngines()
|
||||||
|
if len(activeEngines) > 0 {
|
||||||
|
return activeEngines[0].GetName()
|
||||||
|
}
|
||||||
|
if *seb.cfg.SqlSettings.DisableDatabaseSearch {
|
||||||
|
return "none"
|
||||||
|
}
|
||||||
|
return "database"
|
||||||
|
}
|
||||||
|
|||||||
42
server/platform/services/searchengine/searchengine_test.go
Обычный файл
42
server/platform/services/searchengine/searchengine_test.go
Обычный файл
@@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
package searchengine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/v6/model"
|
||||||
|
"github.com/mattermost/mattermost-server/v6/server/platform/services/searchengine/mocks"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestActiveEngine(t *testing.T) {
|
||||||
|
cfg := &model.Config{}
|
||||||
|
cfg.SetDefaults()
|
||||||
|
|
||||||
|
b := NewBroker(cfg)
|
||||||
|
|
||||||
|
esMock := &mocks.SearchEngineInterface{}
|
||||||
|
esMock.On("IsActive").Return(true)
|
||||||
|
esMock.On("GetName").Return("elasticsearch")
|
||||||
|
|
||||||
|
bleveMock := &mocks.SearchEngineInterface{}
|
||||||
|
bleveMock.On("IsActive").Return(true)
|
||||||
|
bleveMock.On("IsIndexingEnabled").Return(true)
|
||||||
|
bleveMock.On("GetName").Return("bleve")
|
||||||
|
|
||||||
|
assert.Equal(t, "database", b.ActiveEngine())
|
||||||
|
|
||||||
|
b.ElasticsearchEngine = esMock
|
||||||
|
assert.Equal(t, "elasticsearch", b.ActiveEngine())
|
||||||
|
|
||||||
|
b.ElasticsearchEngine = nil
|
||||||
|
b.BleveEngine = bleveMock
|
||||||
|
assert.Equal(t, "bleve", b.ActiveEngine())
|
||||||
|
|
||||||
|
b.BleveEngine = nil
|
||||||
|
*b.cfg.SqlSettings.DisableDatabaseSearch = true
|
||||||
|
|
||||||
|
assert.Equal(t, "none", b.ActiveEngine())
|
||||||
|
}
|
||||||
@@ -353,6 +353,36 @@ exports[`components/DatabaseSettings should match snapshot 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
className="form-group"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
className="control-label col-sm-4"
|
||||||
|
>
|
||||||
|
<MemoizedFormattedMessage
|
||||||
|
defaultMessage="Active Search Backend:"
|
||||||
|
id="admin.database.search_backend.title"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
className="col-sm-8"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
className="form-control"
|
||||||
|
disabled={true}
|
||||||
|
type="text"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="help-text"
|
||||||
|
>
|
||||||
|
<MemoizedFormattedMessage
|
||||||
|
defaultMessage="Shows the currently active backend used for search. Values can be none, database, elasticsearch, bleve etc."
|
||||||
|
id="admin.database.search_backend.help_text"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</SettingsGroup>
|
</SettingsGroup>
|
||||||
<div
|
<div
|
||||||
className="admin-console-save"
|
className="admin-console-save"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
|
||||||
import {recycleDatabaseConnection} from 'actions/admin_actions.jsx';
|
import {recycleDatabaseConnection, ping} from 'actions/admin_actions';
|
||||||
import * as Utils from 'utils/utils';
|
import * as Utils from 'utils/utils';
|
||||||
import {t} from 'utils/i18n';
|
import {t} from 'utils/i18n';
|
||||||
|
|
||||||
@@ -19,6 +19,15 @@ import TextSetting from './text_setting';
|
|||||||
import MigrationsTable from './database';
|
import MigrationsTable from './database';
|
||||||
|
|
||||||
export default class DatabaseSettings extends AdminSettings {
|
export default class DatabaseSettings extends AdminSettings {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
...this.state,
|
||||||
|
searchBackend: '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
getConfigFromState = (config) => {
|
getConfigFromState = (config) => {
|
||||||
// driverName and dataSource are read-only from the UI
|
// driverName and dataSource are read-only from the UI
|
||||||
|
|
||||||
@@ -34,6 +43,17 @@ export default class DatabaseSettings extends AdminSettings {
|
|||||||
return config;
|
return config;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.getSearchBackend().then((searchBackend) => {
|
||||||
|
this.setState({searchBackend});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getSearchBackend() {
|
||||||
|
const res = await ping()();
|
||||||
|
return res.ActiveSearchBackend;
|
||||||
|
}
|
||||||
|
|
||||||
getStateFromConfig(config) {
|
getStateFromConfig(config) {
|
||||||
return {
|
return {
|
||||||
driverName: config.SqlSettings.DriverName,
|
driverName: config.SqlSettings.DriverName,
|
||||||
@@ -368,6 +388,30 @@ export default class DatabaseSettings extends AdminSettings {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className='form-group'>
|
||||||
|
<label
|
||||||
|
className='control-label col-sm-4'
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.database.search_backend.title'
|
||||||
|
defaultMessage='Active Search Backend:'
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<div className='col-sm-8'>
|
||||||
|
<input
|
||||||
|
type='text'
|
||||||
|
className='form-control'
|
||||||
|
value={this.state.searchBackend}
|
||||||
|
disabled={true}
|
||||||
|
/>
|
||||||
|
<div className='help-text'>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.database.search_backend.help_text'
|
||||||
|
defaultMessage='Shows the currently active backend used for search. Values can be none, database, elasticsearch, bleve etc.'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</SettingsGroup>
|
</SettingsGroup>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,8 +7,14 @@ import {shallow} from 'enzyme';
|
|||||||
import DatabaseSettings from 'components/admin_console/database_settings.jsx';
|
import DatabaseSettings from 'components/admin_console/database_settings.jsx';
|
||||||
|
|
||||||
jest.mock('actions/admin_actions.jsx', () => {
|
jest.mock('actions/admin_actions.jsx', () => {
|
||||||
|
const pingFn = () => {
|
||||||
|
return jest.fn(() => {
|
||||||
|
return {ActiveSearchBackend: 'none'};
|
||||||
|
});
|
||||||
|
};
|
||||||
return {
|
return {
|
||||||
recycleDatabaseConnection: jest.fn(),
|
recycleDatabaseConnection: jest.fn(),
|
||||||
|
ping: pingFn,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -741,6 +741,8 @@
|
|||||||
"admin.database.migrations_table.name": "Name",
|
"admin.database.migrations_table.name": "Name",
|
||||||
"admin.database.migrations_table.title": "Applied Schema Migrations",
|
"admin.database.migrations_table.title": "Applied Schema Migrations",
|
||||||
"admin.database.migrations_table.version": "Version",
|
"admin.database.migrations_table.version": "Version",
|
||||||
|
"admin.database.search_backend.help_text": "Shows the currently active backend used for search. Values can be none, database, elasticsearch, bleve etc.",
|
||||||
|
"admin.database.search_backend.title": "Active Search Backend",
|
||||||
"admin.database.title": "Database",
|
"admin.database.title": "Database",
|
||||||
"admin.developer.title": "Developer Settings",
|
"admin.developer.title": "Developer Settings",
|
||||||
"admin.elasticsearch.bulkIndexingTitle": "Bulk Indexing:",
|
"admin.elasticsearch.bulkIndexingTitle": "Bulk Indexing:",
|
||||||
|
|||||||
@@ -2439,6 +2439,7 @@ export default class Client4 {
|
|||||||
ping = () => {
|
ping = () => {
|
||||||
return this.doFetch<{
|
return this.doFetch<{
|
||||||
status: string;
|
status: string;
|
||||||
|
ActiveSearchBackend: string;
|
||||||
}>(
|
}>(
|
||||||
`${this.getBaseRoute()}/system/ping?time=${Date.now()}`,
|
`${this.getBaseRoute()}/system/ping?time=${Date.now()}`,
|
||||||
{method: 'get'},
|
{method: 'get'},
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user