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

Implementing AWS Opensearch serverless connection #3423

Open
wants to merge 2 commits into
base: 2.11.x
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,48 @@ public function getMaxParallelHandles();
*/
public function getMaxRetries();

/**
* Check if SSL verification is enabled.
*
* @return bool
*/
public function isVerifyEnabled();

/**
* Check if AWS Sig4 verification is enabled.
*
* @return bool
*/
public function isAwsSig4Enabled();

/**
* Fetch the AWS Service to be used
*
* @return string
*/
public function getAwsService();

/**
* Get the AWS Region
*
* @return string
*/
public function getAwsRegion();

/**
* Get the AWS Sig4 Key
*
* @return string
*/
public function getAwsSig4Key();

/**
* Get the AWS Sig4 Secret
*
* @return string
*/
public function getAwsSig4Secret();

/**
* Client config options.
*
Expand Down
26 changes: 26 additions & 0 deletions src/module-elasticsuite-core/Client/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ public function __construct(
*/
public function info()
{
if ($this->isAoss()) {
return ['version' => ['number' => '2 ?', 'distribution' => 'AWS OpenSearch Service Serverless']];
}

return $this->getEsClient()->info();
}

Expand All @@ -96,6 +100,10 @@ public function cluster()
*/
public function ping()
{
if ($this->isAoss()) {
return true;
}

return $this->getEsClient()->ping();
}

Expand Down Expand Up @@ -128,6 +136,12 @@ public function indexExists($indexName)
*/
public function putIndexSettings($indexName, $indexSettings)
{
if ($this->isAoss()) {
unset($indexSettings['number_of_replicas']);
unset($indexSettings['refresh_interval']);
unset($indexSettings['translog.durability']);
}

$this->getEsClient()->indices()->putSettings(['index' => $indexName, 'body' => $indexSettings]);
}

Expand Down Expand Up @@ -320,4 +334,16 @@ private function getEsClient(): \OpenSearch\Client

return $this->esClient;
}

/**
* Check if using the AWS OpenSerch Service Serverless, because some operations are not permitted or behaving differently :
* - setting number of replicas per index is not allowed
* - client info is empty
*
* @return bool
*/
private function isAoss(): bool
{
return ($this->clientConfiguration->isAwsSig4Enabled() && $this->clientConfiguration->getAwsService() === 'aoss');
}
}
77 changes: 73 additions & 4 deletions src/module-elasticsuite-core/Client/ClientBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

namespace Smile\ElasticsuiteCore\Client;

use Aws\Credentials\CredentialProvider;
use Aws\Credentials\Credentials;
use OpenSearch\ConnectionPool\Selectors\StickyRoundRobinSelector;

/**
Expand Down Expand Up @@ -48,6 +50,9 @@ class ClientBuilder
'max_parallel_handles' => 100, // As per default Elasticsearch Handler configuration.
'max_retries' => 2,
'verify' => true,
'enable_aws_sig4' => false,
'aws_region' => 'eu-central-1',
'aws_service' => 'es',
];

/**
Expand All @@ -60,6 +65,11 @@ class ClientBuilder
*/
private $namespaceBuilders = [];

/**
* @var callable|null
*/
private $sig4CredentialsProvider = null;

/**
* Constructor.
*
Expand Down Expand Up @@ -119,10 +129,26 @@ public function build(array $options = []): \OpenSearch\Client
$clientBuilder->setTracer($this->logger);
}

$handlerParams = [];
if ($options['max_parallel_handles']) {
$handlerParams = ['max_handles' => (int) $options['max_parallel_handles']];
$handler = \OpenSearch\ClientBuilder::defaultHandler($handlerParams);
$clientBuilder->setHandler($handler);
}
$handler = \OpenSearch\ClientBuilder::defaultHandler($handlerParams);
$clientBuilder->setHandler($handler);

if ($this->isAwsSig4Enabled($options)) {
// The ->setSigV4* methods are only available starting from opensearch/opensearch-php 2.0.1.
try {
$clientBuilder->setSigV4Region($options['aws_region'])
->setSigV4Service($options['aws_service'])
->setSigV4CredentialProvider($this->getSig4CredentialsProvider($options));
} catch (\Exception $exception) {
$this->logger->error($exception->getMessage());
$message = "Cannot configure SigV4 on OpenSearch client. " .
"This can be due to an outdated (<2.0.1) version of the opensearch-project/opensearch-php package.";

throw new \Exception($message . " Error was : " . $exception->getMessage());
}
}

$connectionParams = $this->getConnectionParams($options);
Expand Down Expand Up @@ -168,18 +194,25 @@ private function getHosts(array $options): array

foreach ($options['servers'] as $host) {
if (!empty($host)) {
[$hostname, $port] = array_pad(explode(':', trim($host), 2), 2, 9200);
[$hostname, $port] = array_pad(explode(':', trim(preg_replace('/^https?:\/\//', '', $host)), 2), 2, 9200);
preg_match('/^(https?):\/\//', $host, $matches);

$currentHostConfig = [
'host' => $hostname,
'port' => $port,
'scheme' => isset($options['enable_https_mode']) ? 'https' : $options['scheme'] ?? 'http',
'scheme' => isset($options['enable_https_mode']) ? 'https' : $matches[1] ?? 'http',
];

if ($options['enable_http_auth']) {
$currentHostConfig['user'] = $options['http_auth_user'];
$currentHostConfig['pass'] = $options['http_auth_pwd'];
}

if ($this->isAwsSig4Enabled($options) === true) {
$currentHostConfig['scheme'] = 'https'; // AOSS is alway behind HTTPS url.
$currentHostConfig['host'] = $host; // Use raw host string for AOSS.
}

$hosts[] = $currentHostConfig;
}
}
Expand Down Expand Up @@ -210,4 +243,40 @@ private function getConnectionParams(array $options): array
],
] : [];
}

/**
* Check if AWS Signature v4 should be used for signing the requests
*
* @param array $options The Options
*
* @return bool
*/
private function isAwsSig4Enabled($options)
{
return (((bool) $options['enable_aws_sig4']) === true);
}

/**
* Get CredentialProvider Instance to be used with Sig4 authentication.
* @SuppressWarnings(PHPMD.StaticAccess)
*
* @param array $options The client options
*
* @return callable
*/
private function getSig4CredentialsProvider($options)
{
if (null === $this->sig4CredentialsProvider) {
$this->sig4CredentialsProvider = CredentialProvider::fromCredentials(
new Credentials(
$options['aws_key'],
$options['aws_secret'],
isset($options['aws_token']) ? $options['aws_token'] : null,
isset($options['aws_expires']) ? $options['aws_expires'] : null
)
);
}

return $this->sig4CredentialsProvider;
}
}
45 changes: 45 additions & 0 deletions src/module-elasticsuite-core/Client/ClientConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,46 @@ public function isVerifyEnabled()
return (bool) $this->getElasticsearchClientConfigParam('enable_certificate_validation');
}

/**
* @return bool
*/
public function isAwsSig4Enabled()
{
return (bool) $this->getElasticsearchClientConfigParam('enable_aws_sig4');
}

/**
* @return string
*/
public function getAwsService()
{
return (string) $this->getElasticsearchClientConfigParam('aws_service');
}

/**
* @return string
*/
public function getAwsRegion()
{
return (string) $this->getElasticsearchClientConfigParam('aws_region');
}

/**
* @return string
*/
public function getAwsSig4Key()
{
return (string) $this->getElasticsearchClientConfigParam('aws_key');
}

/**
* @return string
*/
public function getAwsSig4Secret()
{
return (string) $this->getElasticsearchClientConfigParam('aws_secret');
}

/**
* {@inheritDoc}
*/
Expand All @@ -154,6 +194,11 @@ public function getOptions()
'max_parallel_handles' => $this->getMaxParallelHandles(),
'max_retries' => $this->getMaxRetries(),
'verify' => $this->isVerifyEnabled(),
'enable_aws_sig4' => $this->isAwsSig4Enabled(),
'aws_service' => $this->getAwsService(),
'aws_region' => $this->getAwsRegion(),
'aws_key' => $this->getAwsSig4Key(),
'aws_secret' => $this->getAwsSig4Secret(),
];

return $options;
Expand Down
7 changes: 6 additions & 1 deletion src/module-elasticsuite-core/Index/IndexOperation.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,12 @@ public function installIndex(\Smile\ElasticsuiteCore\Api\Index\IndexInterface $i
$indexName = $index->getName();
$indexAlias = $this->indexSettings->getIndexAliasFromIdentifier($indexIdentifier, $store);

$this->client->forceMerge($indexName);
try {
$this->client->forceMerge($indexName);
} catch (\Exception $e) {
$this->logger->error($e->getMessage());
}

$this->client->putIndexSettings($indexName, $this->indexSettings->getInstallIndexSettings($indexIdentifier));

$this->proceedIndexInstall($indexName, $indexAlias);
Expand Down
Loading
Loading