From 44af7aa8d267c9c59d87c278c8a518c44a50d9f3 Mon Sep 17 00:00:00 2001 From: ericuss Date: Mon, 5 Aug 2019 14:21:58 +0200 Subject: [PATCH] Partial checkin --- .../WebHostExtensions.cs | 62 +++++++++++++++++++ .../Infrastructure/MarketingContext.cs | 1 + .../CampaignScenarios.cs | 4 +- .../MarketingScenarioBase.cs | 34 +++++++++- 4 files changed, 97 insertions(+), 4 deletions(-) diff --git a/src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHostExtensions.cs b/src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHostExtensions.cs index e6e1e22a1..2b7f661da 100644 --- a/src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHostExtensions.cs +++ b/src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHostExtensions.cs @@ -60,6 +60,7 @@ namespace Microsoft.AspNetCore.Hosting } catch (Exception ex) { + Console.WriteLine(ex.Message.ToString() + "An error occurred while migrating the database used on context {DbContextName}" + typeof(TContext).Name); logger.LogError(ex, "An error occurred while migrating the database used on context {DbContextName}", typeof(TContext).Name); if (underK8s) { @@ -71,11 +72,72 @@ namespace Microsoft.AspNetCore.Hosting return webHost; } + + public static IWebHost RemoveDbContext(this IWebHost webHost) where TContext : DbContext + { + var underK8s = webHost.IsInKubernetes(); + + using (var scope = webHost.Services.CreateScope()) + { + var services = scope.ServiceProvider; + var logger = services.GetRequiredService>(); + var context = services.GetService(); + + try + { + logger.LogInformation("Deleting the database associated with context {DbContextName}" + typeof(TContext).Name); + + if (underK8s) + { + InvokeRemover(context); + } + else + { + var retries = 10; + var retry = Policy.Handle() + .WaitAndRetry( + retryCount: retries, + sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), + onRetry: (exception, timeSpan, retry, ctx) => + { + Console.WriteLine(" --RETRYING Exception " + exception.Message.ToString()); + logger.LogWarning(exception, "[{prefix}] Exception {ExceptionType} with message {Message} detected on attempt {retry} of {retries}", nameof(TContext), exception.GetType().Name, exception.Message, retry, retries); + }); + + //if the sql server container is not created on run docker compose this + //migration can't fail for network related exception. The retry options for DbContext only + //apply to transient exceptions + // Note that this is NOT applied when running some orchestrators (let the orchestrator to recreate the failing service) + retry.Execute(() => InvokeRemover(context)); + } + + Console.WriteLine("Deleted database associated with context {DbContextName}", typeof(TContext).Name); + logger.LogInformation("Deleted database associated with context {DbContextName}", typeof(TContext).Name); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message.ToString() + "An error occurred while deleting the database used on context {DbContextName}" + typeof(TContext).Name); + logger.LogError(ex, "An error occurred while deleting the database used on context {DbContextName}", typeof(TContext).Name); + if (underK8s) + { + throw; // Rethrow under k8s because we rely on k8s to re-run the pod + } + } + } + + return webHost; + } + private static void InvokeSeeder(Action seeder, TContext context, IServiceProvider services) where TContext : DbContext { context.Database.Migrate(); seeder(context, services); } + private static void InvokeRemover(TContext context) + where TContext : DbContext + { + context.Database.EnsureDeleted(); + } } } diff --git a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs index c4a803212..064a1c3e5 100644 --- a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs +++ b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs @@ -38,6 +38,7 @@ var connectionString = configuration["ConnectionString"]; Console.WriteLine(" -- Connection string"); Console.WriteLine(connectionString); + var optionsBuilder = new DbContextOptionsBuilder() .UseSqlServer(connectionString); // .UseSqlServer("Server=.;Initial Catalog=Microsoft.eShopOnContainers.Services.MarketingDb;Integrated Security=true"); diff --git a/src/Services/Marketing/Marketing.FunctionalTests/CampaignScenarios.cs b/src/Services/Marketing/Marketing.FunctionalTests/CampaignScenarios.cs index abeb4a504..f2643e998 100644 --- a/src/Services/Marketing/Marketing.FunctionalTests/CampaignScenarios.cs +++ b/src/Services/Marketing/Marketing.FunctionalTests/CampaignScenarios.cs @@ -75,7 +75,7 @@ namespace Marketing.FunctionalTests var campaignResponse = await server.CreateClient() .PostAsync(Post.AddNewCampaign, content); - if (int.TryParse(campaignResponse.Headers.Location.Segments[4], out int id)) + if (int.TryParse(campaignResponse.Headers.Location.Segments[3], out int id)) { var response = await server.CreateClient() .DeleteAsync(Delete.CampaignBy(id)); @@ -99,7 +99,7 @@ namespace Marketing.FunctionalTests var campaignResponse = await server.CreateClient() .PostAsync(Post.AddNewCampaign, content); - if (int.TryParse(campaignResponse.Headers.Location.Segments[4], out int id)) + if (int.TryParse(campaignResponse.Headers.Location.Segments[3], out int id)) { fakeCampaignDto.Description = "FakeCampaignUpdatedDescription"; content = new StringContent(JsonConvert.SerializeObject(fakeCampaignDto), Encoding.UTF8, "application/json"); diff --git a/src/Services/Marketing/Marketing.FunctionalTests/MarketingScenarioBase.cs b/src/Services/Marketing/Marketing.FunctionalTests/MarketingScenarioBase.cs index 274db5f69..fdee81ce3 100644 --- a/src/Services/Marketing/Marketing.FunctionalTests/MarketingScenarioBase.cs +++ b/src/Services/Marketing/Marketing.FunctionalTests/MarketingScenarioBase.cs @@ -1,11 +1,16 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; +using Microsoft.eShopOnContainers.Services.Marketing.API; using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; using System.IO; using System.Reflection; +using System.Threading; +using System.Threading.Tasks; namespace Marketing.FunctionalTests { @@ -15,29 +20,54 @@ namespace Marketing.FunctionalTests public TestServer CreateServer() { + + Console.WriteLine(" Creating test server"); var path = Assembly.GetAssembly(typeof(MarketingScenarioBase)) .Location; + Console.WriteLine(" Creating builder"); + var hostBuilder = new WebHostBuilder() .UseContentRoot(Path.GetDirectoryName(path)) .ConfigureAppConfiguration(cb => { - cb.AddJsonFile("appsettings.json", optional: false) + var h = cb.AddJsonFile("appsettings.json", optional: false) .AddEnvironmentVariables(); - }).UseStartup(); + }) + .CaptureStartupErrors(true) + .UseStartup(); + Console.WriteLine(" Created builder"); + var testServer = new TestServer(hostBuilder); + using (var scope = testServer.Services.CreateScope()) + { + var services = scope.ServiceProvider; + + var logger = services.GetRequiredService>(); + var settings = services.GetRequiredService>(); + logger.LogError("connectionString " + settings.Value.ConnectionString); + Console.WriteLine("connectionString " + settings.Value.ConnectionString); + } + testServer.Host + .RemoveDbContext() .MigrateDbContext((context, services) => { var logger = services.GetService>(); + logger.LogError("Migrating MarketingContextSeed"); new MarketingContextSeed() .SeedAsync(context, logger) .Wait(); }); + Console.WriteLine(" Thread to sleep"); + + Thread.Sleep(5000); + Console.WriteLine(" Thread after"); + return testServer; }