Skip to content

Commit

Permalink
PLT-6080 moving clustering to memberlist (mattermost#6499)
Browse files Browse the repository at this point in the history
* PLT-6080 adding cluster discovery service

* Adding memberlist lib

* Adding memberlist lib

* WIP

* WIP

* WIP

* WIP

* Rolling back config changes

* Fixing make file

* Fixing config for cluster

* WIP

* Fixing system console for clustering

* Fixing default config

* Fixing config

* Fixing system console for clustering

* Tweaking hub setting

* Bumping up time

* merging vendor dir

* Updating vendor dir

* Fixing unit test

* Fixing bad merge

* Remove some testing code

* Moving comment

* PLT-6868 adding db ping retry

* Removing unused loc strings

* Adding defer to cancel
  • Loading branch information
coreyhulen committed Jun 19, 2017
1 parent 0157099 commit aa2c1ab
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 59 deletions.
160 changes: 123 additions & 37 deletions components/admin_console/cluster_settings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,33 @@ export default class ClusterSettings extends AdminSettings {

this.getConfigFromState = this.getConfigFromState.bind(this);
this.renderSettings = this.renderSettings.bind(this);
this.overrideHandleChange = this.overrideHandleChange.bind(this);
}

getConfigFromState(config) {
config.ClusterSettings.Enable = this.state.enable;
config.ClusterSettings.InterNodeListenAddress = this.state.interNodeListenAddress;

config.ClusterSettings.InterNodeUrls = this.state.interNodeUrls.split(',');
config.ClusterSettings.InterNodeUrls = config.ClusterSettings.InterNodeUrls.map((url) => {
return url.trim();
});

if (config.ClusterSettings.InterNodeUrls.length === 1 && config.ClusterSettings.InterNodeUrls[0] === '') {
config.ClusterSettings.InterNodeUrls = [];
}

config.ClusterSettings.Enable = this.state.Enable;
config.ClusterSettings.ClusterName = this.state.ClusterName;
config.ClusterSettings.OverrideHostname = this.state.OverrideHostname;
config.ClusterSettings.UseIpAddress = this.state.UseIpAddress;
config.ClusterSettings.UseExperimentalGossip = this.state.UseExperimentalGossip;
config.ClusterSettings.ReadOnlyConfig = this.state.ReadOnlyConfig;
config.ClusterSettings.GossipPort = this.parseIntNonZero(this.state.GossipPort, 8074);
config.ClusterSettings.StreamingPort = this.parseIntNonZero(this.state.StreamingPort, 8075);
return config;
}

getStateFromConfig(config) {
const settings = config.ClusterSettings;

return {
enable: settings.Enable,
interNodeUrls: settings.InterNodeUrls.join(', '),
interNodeListenAddress: settings.InterNodeListenAddress,
Enable: settings.Enable,
ClusterName: settings.ClusterName,
OverrideHostname: settings.OverrideHostname,
UseIpAddress: settings.UseIpAddress,
UseExperimentalGossip: settings.UseExperimentalGossip,
ReadOnlyConfig: settings.ReadOnlyConfig,
GossipPort: settings.GossipPort,
StreamingPort: settings.StreamingPort,
showWarning: false
};
}
Expand Down Expand Up @@ -101,7 +103,7 @@ export default class ClusterSettings extends AdminSettings {
className='alert alert-warning'
>
<i className='fa fa-warning'/>
<FormattedMessage
<FormattedHTMLMessage
id='admin.cluster.should_not_change'
defaultMessage='WARNING: These settings may not sync with the other servers in the cluster. High Availability inter-node communication will not start until you modify the config.json to be identical on all servers and restart Mattermost. Please see the <a href="http:https://docs.mattermost.com/deployment/cluster.html" target="_blank">documentation</a> on how to add or remove a server from the cluster. If you are accessing the System Console through a load balancer and experiencing issues, please see the Troubleshooting Guide in our <a href="http:https://docs.mattermost.com/deployment/cluster.html" target="_blank">documentation</a>.'
/>
Expand All @@ -110,7 +112,7 @@ export default class ClusterSettings extends AdminSettings {
}

var clusterTableContainer = null;
if (this.state.enable) {
if (this.state.Enable) {
clusterTableContainer = (<ClusterTableContainer/>);
}

Expand All @@ -121,12 +123,12 @@ export default class ClusterSettings extends AdminSettings {
<p>
<FormattedMessage
id='admin.cluster.noteDescription'
defaultMessage='Changing properties in this section will require a server restart before taking effect. When High Availability mode is enabled, the System Console is set to read-only and can only be changed from the configuration file.'
defaultMessage='Changing properties in this section will require a server restart before taking effect. When High Availability mode is enabled, the System Console is set to read-only and can only be changed from the configuration file unless ReadOnlyConfig is disabled in the configuration file.'
/>
</p>
{warning}
<BooleanSetting
id='enable'
id='Enable'
label={
<FormattedMessage
id='admin.cluster.enableTitle'
Expand All @@ -139,47 +141,131 @@ export default class ClusterSettings extends AdminSettings {
defaultMessage='When true, Mattermost will run in High Availability mode. Please see <a href="http:https://docs.mattermost.com/deployment/cluster.html" target="_blank">documentation</a> to learn more about configuring High Availability for Mattermost.'
/>
}
value={this.state.enable}
value={this.state.Enable}
onChange={this.overrideHandleChange}
/>
<TextSetting
id='ClusterName'
label={
<FormattedMessage
id='admin.cluster.ClusterName'
defaultMessage='Cluster Name:'
/>
}
placeholder={Utils.localizeMessage('admin.cluster.ClusterNameEx', 'Ex "Production" or "Staging"')}
helpText={
<FormattedMessage
id='admin.cluster.ClusterNameDesc'
defaultMessage='The cluster to join by name. Only nodes with the same cluster name will join together. This is to support Blue-Green deployments or staging pointing to the same database.'
/>
}
value={this.state.ClusterName}
onChange={this.overrideHandleChange}
/>
<TextSetting
id='OverrideHostname'
label={
<FormattedMessage
id='admin.cluster.OverrideHostname'
defaultMessage='Override Hostname:'
/>
}
placeholder={Utils.localizeMessage('admin.cluster.OverrideHostnameEx', 'Ex "app-server-01"')}
helpText={
<FormattedMessage
id='admin.cluster.OverrideHostnameDesc'
defaultMessage='The default value of <blank> will attempt to get the Hostname from the OS or use the IP Address. You can override the hostname of this server with this property. It is not recommended to override the Hostname unless needed. This property can also be set to a specific IP Address if needed.'
/>
}
value={this.state.OverrideHostname}
onChange={this.overrideHandleChange}
/>
<BooleanSetting
id='UseIpAddress'
label={
<FormattedMessage
id='admin.cluster.UseIpAddress'
defaultMessage='Use IP Address:'
/>
}
helpText={
<FormattedHTMLMessage
id='admin.cluster.UseIpAddressDesc'
defaultMessage='When true, the cluster will attempt to communicate via IP Address vs using the hostname.'
/>
}
value={this.state.UseIpAddress}
onChange={this.overrideHandleChange}
/>
<BooleanSetting
id='UseExperimentalGossip'
label={
<FormattedMessage
id='admin.cluster.UseExperimentalGossip'
defaultMessage='Use Experimental Gossip:'
/>
}
helpText={
<FormattedHTMLMessage
id='admin.cluster.UseExperimentalGossipDesc'
defaultMessage='When true, the server will attempt to communicate via the gossip protocol over the gossip port. When false the server will attempt to communicate over the streaming port. When false the gossip port and protocol are still used to determine cluster health.'
/>
}
value={this.state.UseExperimentalGossip}
onChange={this.overrideHandleChange}
/>
<BooleanSetting
id='ReadOnlyConfig'
label={
<FormattedMessage
id='admin.cluster.ReadOnlyConfig'
defaultMessage='Read Only Config:'
/>
}
helpText={
<FormattedHTMLMessage
id='admin.cluster.ReadOnlyConfigDesc'
defaultMessage='When true, the server will reject changes made to the configuration file from the system console. When running in production it is recommened to set this to true.'
/>
}
value={this.state.ReadOnlyConfig}
onChange={this.overrideHandleChange}
disabled={true}
/>
<TextSetting
id='interNodeListenAddress'
id='GossipPort'
label={
<FormattedMessage
id='admin.cluster.interNodeListenAddressTitle'
defaultMessage='Inter-Node Listen Address:'
id='admin.cluster.GossipPort'
defaultMessage='Gossip Port:'
/>
}
placeholder={Utils.localizeMessage('admin.cluster.interNodeListenAddressEx', 'Ex ":8075"')}
placeholder={Utils.localizeMessage('admin.cluster.GossipPortEx', 'Ex "8074"')}
helpText={
<FormattedMessage
id='admin.cluster.interNodeListenAddressDesc'
defaultMessage='The address the server will listen on for communicating with other servers.'
id='admin.cluster.GossipPortDesc'
defaultMessage='The port used for the gossip protocol. Both UDP and TCP should abe allowed on this port.'
/>
}
value={this.state.interNodeListenAddress}
value={this.state.GossipPort}
onChange={this.overrideHandleChange}
disabled={true}
/>
<TextSetting
id='interNodeUrls'
id='StreamingPort'
label={
<FormattedMessage
id='admin.cluster.interNodeUrlsTitle'
defaultMessage='Inter-Node URLs:'
id='admin.cluster.StreamingPort'
defaultMessage='Streaming Port:'
/>
}
placeholder={Utils.localizeMessage('admin.cluster.interNodeUrlsEx', 'Ex "http:https://10.10.10.30, http:https://10.10.10.31"')}
placeholder={Utils.localizeMessage('admin.cluster.StreamingPortEx', 'Ex "8075"')}
helpText={
<FormattedMessage
id='admin.cluster.interNodeUrlsDesc'
defaultMessage='The internal/private URLs of all the Mattermost servers separated by commas.'
id='admin.cluster.StreamingPortDesc'
defaultMessage='The port used for streaming data between servers.'
/>
}
value={this.state.interNodeUrls}
value={this.state.StreamingPort}
onChange={this.overrideHandleChange}
disabled={true}
/>
</SettingsGroup>
);
Expand Down
31 changes: 11 additions & 20 deletions components/admin_console/cluster_table.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {FormattedMessage} from 'react-intl';
import * as Utils from 'utils/utils.jsx';

import statusGreen from 'images/status_green.png';
import statusRed from 'images/status_red.png';
import statusYellow from 'images/status_yellow.png';

export default class ClusterTable extends React.Component {
static propTypes = {
Expand All @@ -34,18 +34,20 @@ export default class ClusterTable extends React.Component {

var version = '';
var configHash = '';
var singleItem = false;

if (this.props.clusterInfos.length) {
version = this.props.clusterInfos[0].version;
configHash = this.props.clusterInfos[0].config_hash;
singleItem = this.props.clusterInfos.length === 1;
}

this.props.clusterInfos.map((clusterInfo) => {
if (clusterInfo.version !== version) {
versionMismatch = (
<img
className='cluster-status'
src={statusRed}
src={statusYellow}
/>
);
}
Expand All @@ -54,7 +56,7 @@ export default class ClusterTable extends React.Component {
configMismatch = (
<img
className='cluster-status'
src={statusRed}
src={statusYellow}
/>
);
}
Expand All @@ -77,34 +79,29 @@ export default class ClusterTable extends React.Component {
clusterInfo.config_hash = Utils.localizeMessage('admin.cluster.unknown', 'unknown');
}

if (clusterInfo.id === '') {
clusterInfo.id = Utils.localizeMessage('admin.cluster.unknown', 'unknown');
}

if (clusterInfo.is_alive > 0) {
if (singleItem) {
status = (
<img
className='cluster-status'
src={statusGreen}
src={statusYellow}
/>
);
} else {
status = (
<img
className='cluster-status'
src={statusRed}
src={statusGreen}
/>
);
}

return (
<tr key={clusterInfo.id}>
<tr key={clusterInfo.ipaddress}>
<td style={{whiteSpace: 'nowrap'}}>{status}</td>
<td style={{whiteSpace: 'nowrap'}}>{clusterInfo.hostname}</td>
<td style={{whiteSpace: 'nowrap'}}>{versionMismatch} {clusterInfo.version}</td>
<td style={{whiteSpace: 'nowrap'}}><div className='config-hash'>{configMismatch} {clusterInfo.config_hash}</div></td>
<td style={{whiteSpace: 'nowrap'}}>{clusterInfo.internode_url}</td>
<td style={{whiteSpace: 'nowrap'}}><div className='config-hash'>{clusterInfo.id}</div></td>
<td style={{whiteSpace: 'nowrap'}}>{clusterInfo.ipaddress}</td>
</tr>
);
});
Expand Down Expand Up @@ -160,13 +157,7 @@ export default class ClusterTable extends React.Component {
<th>
<FormattedMessage
id='admin.cluster.status_table.url'
defaultMessage='Inter-Node URL'
/>
</th>
<th>
<FormattedMessage
id='admin.cluster.status_table.id'
defaultMessage='Node ID'
defaultMessage='Gossip Address'
/>
</th>
</tr>
Expand Down
4 changes: 2 additions & 2 deletions i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,14 +157,14 @@
"admin.cluster.interNodeUrlsEx": "E.g.: \"http:https://10.10.10.30, http:https://10.10.10.31\"",
"admin.cluster.interNodeUrlsTitle": "Inter-Node URLs:",
"admin.cluster.loadedFrom": "This configuration file was loaded from Node ID {clusterId}. Please see the Troubleshooting Guide in our <a href=\"http:https://docs.mattermost.com/deployment/cluster.html\" target='_blank'>documentation</a> if you are accessing the System Console through a load balancer and experiencing issues.",
"admin.cluster.noteDescription": "Changing properties in this section will require a server restart before taking effect. When High Availability mode is enabled, the System Console is set to read-only and can only be changed from the configuration file.",
"admin.cluster.noteDescription": "Changing properties in this section will require a server restart before taking effect. When High Availability mode is enabled, the System Console is set to read-only and can only be changed from the configuration file unless ReadOnlyConfig is disabled in the configuration file.",
"admin.cluster.should_not_change": "WARNING: These settings may not sync with the other servers in the cluster. High Availability inter-node communication will not start until you modify the config.json to be identical on all servers and restart Mattermost. Please see the <a href=\"http:https://docs.mattermost.com/deployment/cluster.html\" target='_blank'>documentation</a> on how to add or remove a server from the cluster. If you are accessing the System Console through a load balancer and experiencing issues, please see the Troubleshooting Guide in our <a href=\"http:https://docs.mattermost.com/deployment/cluster.html\" target='_blank'>documentation</a>.",
"admin.cluster.status_table.config_hash": "Config File MD5",
"admin.cluster.status_table.hostname": "Hostname",
"admin.cluster.status_table.id": "Node ID",
"admin.cluster.status_table.reload": " Reload Cluster Status",
"admin.cluster.status_table.status": "Status",
"admin.cluster.status_table.url": "Inter-Node URL",
"admin.cluster.status_table.url": "Gossip Address",
"admin.cluster.status_table.version": "Version",
"admin.cluster.unknown": "unknown",
"admin.compliance.directoryDescription": "Directory to which compliance reports are written. If blank, will be set to ./data/.",
Expand Down
Binary file added images/status_yellow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit aa2c1ab

Please sign in to comment.