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

Ping Elasticsearch async #14875

Merged
merged 15 commits into from
Dec 14, 2023
Merged
256 changes: 121 additions & 135 deletions src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
using OrchardCore.Deployment;
using OrchardCore.DisplayManagement.Descriptors;
using OrchardCore.DisplayManagement.Handlers;
using OrchardCore.Environment.Shell.Builders;
using OrchardCore.Environment.Shell.Configuration;
using OrchardCore.Modules;
using OrchardCore.Mvc.Core.Utilities;
Expand Down Expand Up @@ -61,138 +60,96 @@ public override void ConfigureServices(IServiceCollection services)
var configuration = _shellConfiguration.GetSection(ConfigSectionName);
var elasticConfiguration = configuration.Get<ElasticConnectionOptions>();

if (CheckOptions(elasticConfiguration, _logger))
if (!CheckOptions(elasticConfiguration, _logger))
{
services.Configure<ElasticConnectionOptions>(o => o.ConfigurationExists = true);

IConnectionPool pool = null;
var uris = elasticConfiguration.Ports.Select(port => new Uri($"{elasticConfiguration.Url}:{port}")).Distinct();

switch (elasticConfiguration.ConnectionType)
{
case "SingleNodeConnectionPool":
pool = new SingleNodeConnectionPool(uris.First());
break;

case "CloudConnectionPool":
BasicAuthenticationCredentials credentials = null;

if (!string.IsNullOrWhiteSpace(elasticConfiguration.Username) && !string.IsNullOrWhiteSpace(elasticConfiguration.Password) && !string.IsNullOrWhiteSpace(elasticConfiguration.CloudId))
{
credentials = new BasicAuthenticationCredentials(elasticConfiguration.Username, elasticConfiguration.Password);
pool = new CloudConnectionPool(elasticConfiguration.CloudId, credentials);
}
break;

case "StaticConnectionPool":
pool = new StaticConnectionPool(uris);
break;

case "SniffingConnectionPool":
pool = new SniffingConnectionPool(uris);
break;

case "StickyConnectionPool":
pool = new StickyConnectionPool(uris);
break;
return;
}

default:
pool = new SingleNodeConnectionPool(uris.First());
break;
}
services.Configure<ElasticConnectionOptions>(o => o.ConfigurationExists = true);
var settings = GetConnectionSettings(elasticConfiguration);

var settings = new ConnectionSettings(pool).ThrowExceptions();
services.AddSingleton<IElasticClient>(new ElasticClient(settings));

if (elasticConfiguration.ConnectionType != "CloudConnectionPool" && !string.IsNullOrWhiteSpace(elasticConfiguration.Username) && !string.IsNullOrWhiteSpace(elasticConfiguration.Password))
{
settings.BasicAuthentication(elasticConfiguration.Username, elasticConfiguration.Password);
}
services.Configure<ElasticsearchOptions>(o =>
{
o.IndexPrefix = configuration.GetValue<string>(nameof(o.IndexPrefix));

if (!string.IsNullOrWhiteSpace(elasticConfiguration.CertificateFingerprint))
{
settings.CertificateFingerprint(elasticConfiguration.CertificateFingerprint);
}
var jsonNode = configuration.GetSection(nameof(o.Analyzers)).AsJsonNode();
var jsonElement = JsonSerializer.Deserialize<JsonElement>(jsonNode);

if (elasticConfiguration.EnableApiVersioningHeader)
var analyzersObject = JsonObject.Create(jsonElement, new JsonNodeOptions()
{
settings.EnableApiVersioningHeader();
}
PropertyNameCaseInsensitive = true,
});

var client = new ElasticClient(settings);
services.AddSingleton<IElasticClient>(client);
services.Configure<ElasticsearchOptions>(o =>
if (analyzersObject != null)
{
o.IndexPrefix = configuration.GetValue<string>(nameof(o.IndexPrefix));

var jsonNode = configuration.GetSection(nameof(o.Analyzers)).AsJsonNode();
var jsonElement = JsonSerializer.Deserialize<JsonElement>(jsonNode);

var analyzersObject = JsonObject.Create(jsonElement, new JsonNodeOptions()
foreach (var analyzer in analyzersObject)
{
PropertyNameCaseInsensitive = true,
});

if (analyzersObject != null)
{
foreach (var analyzer in analyzersObject)
if (analyzer.Value == null)
{
if (analyzer.Value == null)
{
continue;
}

o.Analyzers.Add(analyzer.Key, analyzer.Value.AsObject());
continue;
}
}

if (o.Analyzers.Count == 0)
{
// When no analyzers are configured, we'll define a default analyzer.
o.Analyzers.Add(ElasticsearchConstants.DefaultAnalyzer, new JsonObject
{
["type"] = "standard",
});
o.Analyzers.Add(analyzer.Key, analyzer.Value.AsObject());
}
});
}

try
if (o.Analyzers.Count == 0)
{
var response = client.Ping();

services.Configure<TemplateOptions>(o =>
// When no analyzers are configured, we'll define a default analyzer.
o.Analyzers.Add(ElasticsearchConstants.DefaultAnalyzer, new JsonObject
{
o.MemberAccessStrategy.Register<SearchIndexViewModel>();
o.MemberAccessStrategy.Register<SearchFormViewModel>();
o.MemberAccessStrategy.Register<SearchResultsViewModel>();
["type"] = "standard",
});

services.AddElasticServices();
services.AddScoped<IPermissionProvider, Permissions>();
services.AddScoped<INavigationProvider, AdminMenu>();
services.AddScoped<IDisplayDriver<ISite>, ElasticSettingsDisplayDriver>();
services.AddScoped<IDisplayDriver<Query>, ElasticQueryDisplayDriver>();
services.AddScoped<IContentTypePartDefinitionDisplayDriver, ContentTypePartIndexSettingsDisplayDriver>();
services.AddScoped<IContentPartFieldDefinitionDisplayDriver, ContentPartFieldIndexSettingsDisplayDriver>();
services.AddScoped<ElasticsearchService>();
services.AddScoped<ISearchService>(sp => sp.GetRequiredService<ElasticsearchService>());
services.AddScoped<IAuthorizationHandler, ElasticsearchAuthorizationHandler>();
}
catch (Exception ex)
{
_logger.LogError(ex, "Elasticsearch is enabled but not active because the connection failed.");
}
}
});

services.Configure<TemplateOptions>(o =>
{
o.MemberAccessStrategy.Register<SearchIndexViewModel>();
o.MemberAccessStrategy.Register<SearchFormViewModel>();
o.MemberAccessStrategy.Register<SearchResultsViewModel>();
});

services.AddElasticServices();
services.AddScoped<IPermissionProvider, Permissions>();
services.AddScoped<INavigationProvider, AdminMenu>();
services.AddScoped<IDisplayDriver<ISite>, ElasticSettingsDisplayDriver>();
services.AddScoped<IDisplayDriver<Query>, ElasticQueryDisplayDriver>();
services.AddScoped<IContentTypePartDefinitionDisplayDriver, ContentTypePartIndexSettingsDisplayDriver>();
services.AddScoped<IContentPartFieldDefinitionDisplayDriver, ContentPartFieldIndexSettingsDisplayDriver>();
services.AddScoped<ElasticsearchService>();
services.AddScoped<ISearchService>(sp => sp.GetRequiredService<ElasticsearchService>());
services.AddScoped<IAuthorizationHandler, ElasticsearchAuthorizationHandler>();
}

public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider)
private static ConnectionSettings GetConnectionSettings(ElasticConnectionOptions elasticConfiguration)
{
var options = serviceProvider.GetRequiredService<IOptions<ElasticConnectionOptions>>().Value;
var pool = GetConnectionPool(elasticConfiguration);

var settings = new ConnectionSettings(pool);

if (!options.ConfigurationExists)
if (elasticConfiguration.ConnectionType != "CloudConnectionPool" && !string.IsNullOrWhiteSpace(elasticConfiguration.Username) && !string.IsNullOrWhiteSpace(elasticConfiguration.Password))
{
return;
settings.BasicAuthentication(elasticConfiguration.Username, elasticConfiguration.Password);
}

if (!string.IsNullOrWhiteSpace(elasticConfiguration.CertificateFingerprint))
{
settings.CertificateFingerprint(elasticConfiguration.CertificateFingerprint);
}

if (elasticConfiguration.EnableApiVersioningHeader)
{
settings.EnableApiVersioningHeader();
}

return settings;
}

public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider)
{
var adminControllerName = typeof(AdminController).ControllerName();

routes.MapAreaControllerRoute(
Expand Down Expand Up @@ -240,53 +197,88 @@ public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder ro

private static bool CheckOptions(ElasticConnectionOptions elasticConnectionOptions, ILogger logger)
{
var optionsAreValid = true;

if (elasticConnectionOptions == null)
{
logger.LogError("Elasticsearch is enabled but not active because the configuration is missing.");
return false;
}

var optionsAreValid = true;

if (string.IsNullOrWhiteSpace(elasticConnectionOptions.Url))
{
logger.LogError("Elasticsearch is enabled but not active because the 'Url' is missing or empty in application configuration.");
optionsAreValid = false;
}

if (elasticConnectionOptions.Ports.Length == 0)
if (elasticConnectionOptions.Ports?.Length == 0)
{
logger.LogError("Elasticsearch is enabled but not active because a port is missing in application configuration.");
optionsAreValid = false;
}

return optionsAreValid;
}

private static IConnectionPool GetConnectionPool(ElasticConnectionOptions elasticConfiguration)
{
var uris = elasticConfiguration.Ports.Select(port => new Uri($"{elasticConfiguration.Url}:{port}")).Distinct();
IConnectionPool pool = null;
switch (elasticConfiguration.ConnectionType)
{
case "SingleNodeConnectionPool":
pool = new SingleNodeConnectionPool(uris.First());
break;

case "CloudConnectionPool":
if (!string.IsNullOrWhiteSpace(elasticConfiguration.Username) && !string.IsNullOrWhiteSpace(elasticConfiguration.Password) && !string.IsNullOrWhiteSpace(elasticConfiguration.CloudId))
{
var credentials = new BasicAuthenticationCredentials(elasticConfiguration.Username, elasticConfiguration.Password);
pool = new CloudConnectionPool(elasticConfiguration.CloudId, credentials);
}
break;

case "StaticConnectionPool":
pool = new StaticConnectionPool(uris);
break;

case "SniffingConnectionPool":
pool = new SniffingConnectionPool(uris);
break;

case "StickyConnectionPool":
pool = new StickyConnectionPool(uris);
break;

default:
pool = new SingleNodeConnectionPool(uris.First());
break;
}

return pool;
}
}

[RequireFeatures("OrchardCore.Deployment")]
public class DeploymentStartup : StartupBase
{
public override void ConfigureServices(IServiceCollection services)
{
if (services.Any(d => d.GetImplementationType() == typeof(ElasticsearchService)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok that's the part that @sebastienros doesn't like. Then, I'm guessing we are removing this everywhere else too?

{
services.AddTransient<IDeploymentSource, ElasticIndexDeploymentSource>();
services.AddSingleton<IDeploymentStepFactory>(new DeploymentStepFactory<ElasticIndexDeploymentStep>());
services.AddScoped<IDisplayDriver<DeploymentStep>, ElasticIndexDeploymentStepDriver>();
services.AddTransient<IDeploymentSource, ElasticIndexDeploymentSource>();
services.AddSingleton<IDeploymentStepFactory>(new DeploymentStepFactory<ElasticIndexDeploymentStep>());
services.AddScoped<IDisplayDriver<DeploymentStep>, ElasticIndexDeploymentStepDriver>();

services.AddTransient<IDeploymentSource, ElasticSettingsDeploymentSource>();
services.AddSingleton<IDeploymentStepFactory>(new DeploymentStepFactory<ElasticSettingsDeploymentStep>());
services.AddScoped<IDisplayDriver<DeploymentStep>, ElasticSettingsDeploymentStepDriver>();
services.AddTransient<IDeploymentSource, ElasticSettingsDeploymentSource>();
services.AddSingleton<IDeploymentStepFactory>(new DeploymentStepFactory<ElasticSettingsDeploymentStep>());
services.AddScoped<IDisplayDriver<DeploymentStep>, ElasticSettingsDeploymentStepDriver>();

services.AddTransient<IDeploymentSource, ElasticIndexRebuildDeploymentSource>();
services.AddSingleton<IDeploymentStepFactory>(new DeploymentStepFactory<ElasticIndexRebuildDeploymentStep>());
services.AddScoped<IDisplayDriver<DeploymentStep>, ElasticIndexRebuildDeploymentStepDriver>();
services.AddTransient<IDeploymentSource, ElasticIndexRebuildDeploymentSource>();
services.AddSingleton<IDeploymentStepFactory>(new DeploymentStepFactory<ElasticIndexRebuildDeploymentStep>());
services.AddScoped<IDisplayDriver<DeploymentStep>, ElasticIndexRebuildDeploymentStepDriver>();

services.AddTransient<IDeploymentSource, ElasticIndexResetDeploymentSource>();
services.AddSingleton<IDeploymentStepFactory>(new DeploymentStepFactory<ElasticIndexResetDeploymentStep>());
services.AddScoped<IDisplayDriver<DeploymentStep>, ElasticIndexResetDeploymentStepDriver>();
}
services.AddTransient<IDeploymentSource, ElasticIndexResetDeploymentSource>();
services.AddSingleton<IDeploymentStepFactory>(new DeploymentStepFactory<ElasticIndexResetDeploymentStep>());
services.AddScoped<IDisplayDriver<DeploymentStep>, ElasticIndexResetDeploymentStepDriver>();
}
}

Expand All @@ -295,10 +287,7 @@ public class ElasticWorkerStartup : StartupBase
{
public override void ConfigureServices(IServiceCollection services)
{
if (services.Any(d => d.GetImplementationType() == typeof(ElasticsearchService)))
{
services.AddSingleton<IBackgroundTask, IndexingBackgroundTask>();
}
services.AddSingleton<IBackgroundTask, IndexingBackgroundTask>();
}
}

Expand All @@ -307,12 +296,9 @@ public class ElasticContentPickerStartup : StartupBase
{
public override void ConfigureServices(IServiceCollection services)
{
if (services.Any(d => d.GetImplementationType() == typeof(ElasticsearchService)))
{
services.AddScoped<IContentPickerResultProvider, ElasticContentPickerResultProvider>();
services.AddScoped<IContentPartFieldDefinitionDisplayDriver, ContentPickerFieldElasticEditorSettingsDriver>();
services.AddShapeAttributes<ElasticContentPickerShapeProvider>();
}
services.AddScoped<IContentPickerResultProvider, ElasticContentPickerResultProvider>();
services.AddScoped<IContentPartFieldDefinitionDisplayDriver, ContentPickerFieldElasticEditorSettingsDriver>();
services.AddShapeAttributes<ElasticContentPickerShapeProvider>();
}
}
}