Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server type detection #2835

Merged
merged 10 commits into from
Jun 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .wp-env.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"mappings": {
"wp-content/mu-plugins/unique-index-name.php": "./tests/cypress/wordpress-files/test-mu-plugins/unique-index-name.php",
"wp-content/plugins/fake-new-activation.php": "./tests/cypress/wordpress-files/test-plugins/fake-new-activation.php",
"wp-content/plugins/unsupported-server-software.php": "./tests/cypress/wordpress-files/test-plugins/unsupported-server-software.php",
"wp-content/plugins/unsupported-elasticsearch-version.php": "./tests/cypress/wordpress-files/test-plugins/unsupported-elasticsearch-version.php",
"wp-content/uploads/content-example.xml": "./tests/cypress/wordpress-files/test-docs/content-example.xml"
}
Expand Down
20 changes: 20 additions & 0 deletions docs/compatibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
ElasticPress requirements can be found in the [Requirements section](https://github.com/10up/ElasticPress#requirements) in our README. If your solution relies on a different server or version, you may find additional information in this document.

## Elasticsearch (Unsupported Versions)

The ElasticPress team updates minimum and maximum required versions for Elasticsearch when required to implement new features and ensure security. ElasticPress will warn you if the version detected is newer than the latest tested version, but all plugin functionality will continue to work. If you are succesfully using a more recent version of Elasticsearch than ElasticPress currently supports, please share your findings with us via a [Github issue](https://github.com/10up/ElasticPress/issues).

## OpenSearch

ElasticPress does not officially support OpenSearch in this version of the plugin, but our initial testing indicates basic ElasticPress functionality may be possible using OpenSearch. We do not recommend using OpenSearch in production at this time. Please provide any feedback about compability issues via a [Github issue](https://github.com/10up/ElasticPress/issues) and we will include it in a future OpenSearch compatibility release, should one occur.

Currently, if you want to run ElasticPress with any version of OpenSearch, you need to use a snippet like the following to set the compatible Elasticsearch mapping version for the version of OpenSearch you're running. Otherwise, ElasticPress will detect the version of OpenSearch and attempt to set the oldest possible Elasticsearch mapping, due to version number differences between Elasticsearch and OpenSearch.

```
add_filter(
'ep_elasticsearch_version',
function() {
return '7.10';
}
);
```
5 changes: 4 additions & 1 deletion docs/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"install": {
"title": "Install"
},
"compatibility": {
"title": "Compatibility"
},
"features": {
"title": "Features"
},
Expand Down Expand Up @@ -30,6 +33,6 @@
"title": "Theme Integration"
},
"indexing-process": {
"title": "Indexing Process"
"title": "Indexing Process"
}
}
48 changes: 48 additions & 0 deletions includes/classes/AdminNotices.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class AdminNotices {
'host_error',
'es_below_compat',
'es_above_compat',
'different_server_type',
'need_setup',
'no_sync',
'upgrade_sync',
Expand Down Expand Up @@ -523,6 +524,53 @@ protected function process_es_above_compat_notice() {
}
}

/**
* Server software different from Elasticsearch warning.
*
* Type: warning
* Dismiss: Anywhere
* Show: All screens
*
* @since 4.2.1
* @return array|bool
*/
protected function process_different_server_type_notice() {
if ( Utils\is_epio() ) {
return false;
}

$host = Utils\get_host();

if ( empty( $host ) ) {
return false;
}

$server_type = Elasticsearch::factory()->get_server_type();

if ( false === $server_type || 'elasticsearch' === $server_type ) {
return false;
}

$dismiss = Utils\get_option( 'ep_hide_different_server_type_notice', false );

if ( $dismiss ) {
return false;
}

$doc_url = 'https://10up.github.io/ElasticPress/tutorial-compatibility.html';
$html = sprintf(
/* translator: Document page URL */
__( 'Your server software is not supported. To learn more about server compatibility please <a href="%s">visit our documentation</a>.', 'elasticpress' ),
esc_url( $doc_url )
);

return [
'html' => $html,
'type' => 'warning',
'dismiss' => true,
];
}

/**
* Host error notification. Shows when EP can't reach ES host.
*
Expand Down
243 changes: 152 additions & 91 deletions includes/classes/Elasticsearch.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ class Elasticsearch {
*/
public $elasticsearch_version = null;

/**
* Server type (elasticsearch, opensearch, etc.)
*
* @var string
*/
public $server_type = 'elasticsearch';

/**
* Return singleton instance of class
*
Expand Down Expand Up @@ -220,6 +227,28 @@ public function get_elasticsearch_version( $force = false ) {
return apply_filters( 'ep_elasticsearch_version', $info['version'] );
}

/**
* Get server type. We cache this so we don't have to do it every time.
*
* @param bool $force Bust cache or not.
* @since 4.2.1
* @return string|bool
*/
public function get_server_type( $force = false ) {

$info = $this->get_elasticsearch_info( $force );

/**
* Filter server type
*
* @hook ep_server_type
* @param {string} $type Type (elasticsearch, opensearch, others)
* @return {string} New type
* @since 4.2.1
*/
return apply_filters( 'ep_server_type', $info['server_type'] );
}

/**
* Get Elasticsearch plugins. We cache this so we don't have to do it every time.
*
Expand Down Expand Up @@ -1269,121 +1298,153 @@ public function parse_api_response( $response ) {
}

/**
* Get ES plugins and version, cache everything
* Set ES plugins and version, detect server type, and cache everything
*
* @param bool $force Bust cache or not.
* @since 2.2
* @since 4.2.1
* @param bool $force Bust cache or not.
* @return array
*/
public function get_elasticsearch_info( $force = false ) {
if ( ! empty( Utils\get_host() ) && ( $force || null === $this->elasticsearch_version || null === $this->elasticsearch_plugins ) ) {
public function set_elasticsearch_info( $force = false ) {
if ( empty( Utils\get_host() ) ) {
return;
}

if ( ! $force && null !== $this->elasticsearch_version && null !== $this->elasticsearch_plugins ) {
return;
}

// Get ES info from cache if available. If we are forcing, then skip cache check.
if ( $force ) {
$es_info = false;
// Get ES info from cache if available. If we are forcing, then skip cache check.
if ( ! $force ) {
if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
$es_info = get_site_transient( 'ep_es_info' );
} else {
if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
$es_info = get_site_transient( 'ep_es_info' );
} else {
$es_info = get_transient( 'ep_es_info' );
}
$es_info = get_transient( 'ep_es_info' );
}

if ( ! empty( $es_info ) ) {
// Set ES info from cache.
$this->elasticsearch_version = $es_info['version'];
$this->elasticsearch_plugins = $es_info['plugins'];
} else {
$path = '_nodes/plugins';

$request = $this->remote_request( $path, array( 'method' => 'GET' ) );

if ( is_wp_error( $request ) || 200 !== wp_remote_retrieve_response_code( $request ) ) {
$this->elasticsearch_version = false;
$this->elasticsearch_plugins = false;

/**
* Try a different endpoint in case the plugins url is restricted
*
* @since 2.2.1
*/

$request = $this->remote_request( '', array( 'method' => 'GET' ) );

if ( ! is_wp_error( $request ) && 200 === wp_remote_retrieve_response_code( $request ) ) {
$response_body = wp_remote_retrieve_body( $request );
$response = json_decode( $response_body, true );

try {
$this->elasticsearch_version = $response['version']['number'];
} catch ( Exception $e ) {
// Do nothing.
}
}
} else {
$response = json_decode( wp_remote_retrieve_body( $request ), true );
$this->server_type = $es_info['server_type'];
return;
}
}

$this->elasticsearch_plugins = [];
$this->elasticsearch_version = false;
$path = '_nodes/plugins';

if ( isset( $response['nodes'] ) ) {
$request = $this->remote_request( $path, array( 'method' => 'GET' ) );

foreach ( $response['nodes'] as $node ) {
// Save version of last node. We assume all nodes are same version.
$this->elasticsearch_version = $node['version'];
if ( is_wp_error( $request ) || 200 !== wp_remote_retrieve_response_code( $request ) ) {
$this->elasticsearch_version = false;
$this->elasticsearch_plugins = false;

if ( isset( $node['plugins'] ) && is_array( $node['plugins'] ) ) {
/**
* Try a different endpoint in case the plugins url is restricted
*
* @since 2.2.1
*/

foreach ( $node['plugins'] as $plugin ) {
$request = $this->remote_request( '', array( 'method' => 'GET' ) );

$this->elasticsearch_plugins[ $plugin['name'] ] = $plugin['version'];
}
if ( ! is_wp_error( $request ) && 200 === wp_remote_retrieve_response_code( $request ) ) {
$response_body = wp_remote_retrieve_body( $request );
$response = json_decode( $response_body, true );

break;
}
}
try {
$this->elasticsearch_version = $response['version']['number'];
if ( ! empty( $response['version']['distribution'] ) ) {
$this->server_type = $response['version']['distribution'];
}
} catch ( \Exception $e ) {
// Do nothing.
}
}
return;
}

/**
* Cache ES info
*
* @since 2.3.1
*/
$response = json_decode( wp_remote_retrieve_body( $request ), true );

/**
* Filter elasticsearch info cache expiration
*
* @hook ep_es_info_cache_expiration
* @param {int} $time Cache time in seconds
* @return {int} New cache time
*/
if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
set_site_transient(
'ep_es_info',
array(
'version' => $this->elasticsearch_version,
'plugins' => $this->elasticsearch_plugins,
),
apply_filters( 'ep_es_info_cache_expiration', ( 5 * MINUTE_IN_SECONDS ) )
);
} else {
set_transient(
'ep_es_info',
array(
'version' => $this->elasticsearch_version,
'plugins' => $this->elasticsearch_plugins,
),
apply_filters( 'ep_es_info_cache_expiration', ( 5 * MINUTE_IN_SECONDS ) )
);
$this->elasticsearch_plugins = [];
$this->elasticsearch_version = false;

if ( isset( $response['nodes'] ) ) {
$node = end( $response['nodes'] );
// Save version of last node. We assume all nodes are same version.
$this->elasticsearch_version = $node['version'];

if ( isset( $node['plugins'] ) && is_array( $node['plugins'] ) ) {
foreach ( $node['plugins'] as $plugin ) {
$this->elasticsearch_plugins[ $plugin['name'] ] = $plugin['version'];
}
}
if ( isset( $node['modules'] )
&& is_array( $node['modules'] )
&& ! empty( $node['modules'] )
&& ! empty( $node['modules'][0]['opensearch_version'] )
) {
$this->server_type = 'opensearch';
}
}

return array(
'plugins' => $this->elasticsearch_plugins,
'version' => $this->elasticsearch_version,
);
/**
* Cache ES info
*
* @since 2.3.1
*/
$this->cache_elasticsearch_info();
}

/**
* Return ES plugins, version and type.
*
* This function also sets those values in the object instance, getting it from cache
* or not, according to `$force` value.
*
* @param bool $force Bust cache or not.
* @since 2.2
* @return array
*/
public function get_elasticsearch_info( $force = false ) {
$this->set_elasticsearch_info( $force );
return [
'plugins' => $this->elasticsearch_plugins,
'version' => $this->elasticsearch_version,
'server_type' => $this->server_type,
];
}

/**
* Cache the ES info.
*
* @since 4.2.1
*/
protected function cache_elasticsearch_info() {
/**
* Filter elasticsearch info cache expiration
*
* @hook ep_es_info_cache_expiration
* @param {int} $time Cache time in seconds
* @return {int} New cache time
*/
if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
set_site_transient(
'ep_es_info',
array(
'version' => $this->elasticsearch_version,
'plugins' => $this->elasticsearch_plugins,
'server_type' => $this->server_type,
),
apply_filters( 'ep_es_info_cache_expiration', ( 5 * MINUTE_IN_SECONDS ) )
);
} else {
set_transient(
'ep_es_info',
array(
'version' => $this->elasticsearch_version,
'plugins' => $this->elasticsearch_plugins,
'server_type' => $this->server_type,
),
apply_filters( 'ep_es_info_cache_expiration', ( 5 * MINUTE_IN_SECONDS ) )
);
}
}

/**
Expand Down
Loading