diff --git a/includes/classes/Command.php b/includes/classes/Command.php index b3eda79261..f0aff11d70 100644 --- a/includes/classes/Command.php +++ b/includes/classes/Command.php @@ -1482,6 +1482,65 @@ public function settings_reset( $args, $assoc_args ) { WP_CLI::line( esc_html__( 'Settings deleted.', 'elasticpress' ) ); } + /** + * Migrate content from an Elasticsearch server to another.. + * + * ## OPTIONS + * + * --target= + * : The target host full URL + * + * @since 4.2.0 + * + * @param array $args Positional CLI args. + * @param array $assoc_args Associative CLI args. + */ + public function migrate( $args, $assoc_args ) { + $source_host = trailingslashit( Utils\get_host( true ) ); + $target_host = esc_url_raw( trim( $assoc_args['target'] ) ); + + $set_target_as_host = function () use ( $target_host ) { + return $target_host; + }; + + WP_CLI::line( esc_html__( 'Switching context to target server.', 'elasticpress' ) ); + add_filter( 'ep_host', $set_target_as_host ); + + // Check if server is available and then send the mapping. + $this->connect_check(); + $this->put_mapping_helper( $args, [ 'network-wide' => true ] ); + + $index_names = $this->get_index_names(); + foreach ( $index_names as $index ) { + WP_CLI::line( + sprintf( + /* translators: Index name */ + esc_html__( 'Reindexing %s...', 'elasticpress' ), + esc_html( $index ) + ) + ); + $request = Elasticsearch::factory()->reindex( $index, $source_host, $target_host ); + if ( is_wp_error( $request ) ) { + WP_CLI::error( + sprintf( + /* translators: 1. Error message, 2. Error code */ + esc_html__( '%1$s (Code %2$s)', 'elasticpress' ), + $request->get_error_message(), + $request->get_error_code() + ) + ); + } else { + WP_CLI::log( var_export( $request, true ) ); + } + // @todo Output details of successful reindexes. + } + + update_site_option( 'ep_host', $target_host ); + remove_filter( 'ep_host', $set_target_as_host ); + + WP_CLI::success( esc_html__( 'Done.', 'elasticpress' ) ); + } + /** * Print an HTTP response. * diff --git a/includes/classes/Elasticsearch.php b/includes/classes/Elasticsearch.php index 4081c746da..95cc97d083 100644 --- a/includes/classes/Elasticsearch.php +++ b/includes/classes/Elasticsearch.php @@ -1537,6 +1537,68 @@ public function add_elasticpress_version_to_user_agent( $user_agent ) { return $user_agent; } + /** + * Reindex content + * + * @since 4.2.0 + * + * @param string $index Index name + * @param string $source Source host URL + * @return WP_Error|string + */ + public function reindex( $index, $source ) { + $parsed_url = wp_parse_url( $source ); + + $scheme = isset( $parsed_url['scheme'] ) ? $parsed_url['scheme'] . '://' : ''; + $host = isset( $parsed_url['host'] ) ? $parsed_url['host'] : ''; + $port = isset( $parsed_url['port'] ) ? ':' . $parsed_url['port'] : ':80'; + $user = isset( $parsed_url['user'] ) ? $parsed_url['user'] : ''; + $pass = isset( $parsed_url['pass'] ) ? ':' . $parsed_url['pass'] : ''; + $pass = ( $user || $pass ) ? "$pass@" : ''; + $path = isset( $parsed_url['path'] ) ? $parsed_url['path'] : ''; + $query = isset( $parsed_url['query'] ) ? '?' . $parsed_url['query'] : ''; + $fragment = isset( $parsed_url['fragment'] ) ? '#' . $parsed_url['fragment'] : ''; + $source = "$scheme$user$pass$host$port$path$query$fragment"; + + $content = [ + 'max_docs' => 1000, + 'source' => [ + 'remote' => [ + 'host' => 'http://elasticsearch5:9200/', // @todo untrailingslashit( $source ), + ], + 'index' => $index, + ], + 'dest' => [ + 'index' => $index, + ], + ]; + + $request_args = [ + 'body' => wp_json_encode( $content ), + 'method' => 'POST', + 'headers' => [ + 'Content-Type' => 'application/json', + ], + ]; + + $request = $this->remote_request( '_reindex', $request_args, [], 'reindex' ); + + if ( is_wp_error( $request ) ) { + return $request; + } + + $response_code = absint( wp_remote_retrieve_response_code( $request ) ); + $is_valid_res = ( $response_code >= 200 && $response_code <= 299 ); + + $response_body = wp_remote_retrieve_body( $request ); + + if ( ! $is_valid_res ) { + return new \WP_Error( $response_code, $response_body ); + } + + return $response_body; + } + /** * Query logging. Don't log anything to the queries property when * WP_DEBUG is not enabled. Calls action 'ep_add_query_log' if you