diff --git a/.gitignore b/.gitignore index 870ddfc7..2d0804d3 100644 --- a/.gitignore +++ b/.gitignore @@ -340,4 +340,7 @@ appsettings.json **/MsDeploy -*.feature.cs \ No newline at end of file +*.feature.cs + +# ignore local settings file that may contain secrets. +/Solutions/Marain.Tenancy.Host.AspNetCore/appsettings.Development.json diff --git a/Solutions/Docker.Compose/.dockerignore b/Solutions/Docker.Compose/.dockerignore new file mode 100644 index 00000000..3729ff0c --- /dev/null +++ b/Solutions/Docker.Compose/.dockerignore @@ -0,0 +1,25 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md \ No newline at end of file diff --git a/Solutions/Docker.Compose/docker-compose.dcproj b/Solutions/Docker.Compose/docker-compose.dcproj new file mode 100644 index 00000000..4726c6c7 --- /dev/null +++ b/Solutions/Docker.Compose/docker-compose.dcproj @@ -0,0 +1,20 @@ + + + + 2.1 + Linux + e6ef3336-38d7-43ae-9840-6359f20db9c8 + LaunchBrowser + {Scheme}://localhost:{ServicePort}/swagger + marain.tenancy.host.aspnetcore + + + + docker-compose.yml + + + + + + + \ No newline at end of file diff --git a/Solutions/Docker.Compose/docker-compose.override.yml b/Solutions/Docker.Compose/docker-compose.override.yml new file mode 100644 index 00000000..367efe70 --- /dev/null +++ b/Solutions/Docker.Compose/docker-compose.override.yml @@ -0,0 +1,8 @@ +version: '3.4' + +services: + marain.tenancy.host.aspnetcore: + environment: + - ASPNETCORE_ENVIRONMENT=Development + ports: + - "80" diff --git a/Solutions/Docker.Compose/docker-compose.yml b/Solutions/Docker.Compose/docker-compose.yml new file mode 100644 index 00000000..85cc8e33 --- /dev/null +++ b/Solutions/Docker.Compose/docker-compose.yml @@ -0,0 +1,15 @@ +version: '3.4' + +services: + marain.tenancy.host.aspnetcore: + image: ${DOCKER_REGISTRY-}maraintenancyhostaspnetcore + build: + context: ./../ + dockerfile: Marain.Tenancy.Host.AspNetCore/Dockerfile + + marain.tenancy.host.aspnetcore-dapr: + image: "daprio/daprd:latest" + command: [ "./daprd", "-app-id", "marain.tenancy.host.aspnetcore", "-app-port", "80" ] + depends_on: + - marain.tenancy.host.aspnetcore + network_mode: "service:marain.tenancy.host.aspnetcore" \ No newline at end of file diff --git a/Solutions/Marain.Tenancy.Host.AspNetCore/Dockerfile b/Solutions/Marain.Tenancy.Host.AspNetCore/Dockerfile new file mode 100644 index 00000000..ab8d4d6f --- /dev/null +++ b/Solutions/Marain.Tenancy.Host.AspNetCore/Dockerfile @@ -0,0 +1,23 @@ +#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base +WORKDIR /app +EXPOSE 80 + +FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build +WORKDIR /src +COPY ["Marain.Tenancy.Host.AspNetCore/Marain.Tenancy.Host.AspNetCore.csproj", "Marain.Tenancy.Host.AspNetCore/"] +COPY ["Marain.Tenancy.Hosting.AspNetCore/Marain.Tenancy.Hosting.AspNetCore.csproj", "Marain.Tenancy.Hosting.AspNetCore/"] +COPY ["Marain.Tenancy.OpenApi.Service/Marain.Tenancy.OpenApi.Service.csproj", "Marain.Tenancy.OpenApi.Service/"] +RUN dotnet restore "Marain.Tenancy.Host.AspNetCore/Marain.Tenancy.Host.AspNetCore.csproj" +COPY . . +WORKDIR "/src/Marain.Tenancy.Host.AspNetCore" +RUN dotnet build "Marain.Tenancy.Host.AspNetCore.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "Marain.Tenancy.Host.AspNetCore.csproj" -c Release -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Marain.Tenancy.Host.AspNetCore.dll"] \ No newline at end of file diff --git a/Solutions/Marain.Tenancy.Host.AspNetCore/Marain.Tenancy.Host.AspNetCore.csproj b/Solutions/Marain.Tenancy.Host.AspNetCore/Marain.Tenancy.Host.AspNetCore.csproj new file mode 100644 index 00000000..2d3c836b --- /dev/null +++ b/Solutions/Marain.Tenancy.Host.AspNetCore/Marain.Tenancy.Host.AspNetCore.csproj @@ -0,0 +1,19 @@ + + + + net6.0 + ..\docker-compose.dcproj + Linux + false + + + + + + + + + + + + diff --git a/Solutions/Marain.Tenancy.Host.AspNetCore/Program.cs b/Solutions/Marain.Tenancy.Host.AspNetCore/Program.cs new file mode 100644 index 00000000..36b4916a --- /dev/null +++ b/Solutions/Marain.Tenancy.Host.AspNetCore/Program.cs @@ -0,0 +1,25 @@ +namespace Marain.Tenancy.Host.AspNetCore +{ + using Microsoft.AspNetCore.Hosting; + using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.Hosting; + + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.AddEnvironmentVariables(); + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} \ No newline at end of file diff --git a/Solutions/Marain.Tenancy.Host.AspNetCore/Properties/launchSettings.json b/Solutions/Marain.Tenancy.Host.AspNetCore/Properties/launchSettings.json new file mode 100644 index 00000000..06ce2152 --- /dev/null +++ b/Solutions/Marain.Tenancy.Host.AspNetCore/Properties/launchSettings.json @@ -0,0 +1,35 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:44494", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Marain.Tenancy.Host.AspNetCore": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": "true", + "applicationUrl": "http://localhost:5000" + }, + "Docker": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", + "publishAllPorts": true + } + } +} \ No newline at end of file diff --git a/Solutions/Marain.Tenancy.Host.AspNetCore/Startup.cs b/Solutions/Marain.Tenancy.Host.AspNetCore/Startup.cs new file mode 100644 index 00000000..5f52e34d --- /dev/null +++ b/Solutions/Marain.Tenancy.Host.AspNetCore/Startup.cs @@ -0,0 +1,73 @@ +namespace Marain.Tenancy.Host.AspNetCore +{ + using System; + + using Corvus.Storage.Azure.BlobStorage; + + using Menes; + using Menes.Auditing.AuditLogSinks.Development; + using Menes.Hosting.AspNetCore; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Hosting; + + public class Startup + { + public IConfiguration Configuration { get; } + + public Startup(IConfiguration configuration) + { + this.Configuration = configuration; + } + + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddTenancyApiWithAspNetPipelineHosting(ConfigureOpenApiHost); + services.AddOpenApiAuditing(); + + BlobContainerConfiguration rootStorageConfiguration = this.Configuration + .GetSection("RootBlobStorageConfiguration") + .Get(); + + services.AddTenantStoreOnAzureBlobStorage(rootStorageConfiguration); + +#if DEBUG + services.AddAuditLogSink(); +#endif + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseMenesCatchAll(); + } + + // TODO: consolidate with functions startup code. + // This fixes a bug from that - the 2nd exception handler was wrong on two counts: + // 1. wrong exception type: if config is non-null and config.Documents is null, that's + // not ArgumentNullException + // 2. wrong argument order: we had the nameof and message flipped + // In any case, this startup is likely to be needed by any host, so we should put it + // somewhere common. + private static void ConfigureOpenApiHost(IOpenApiHostConfiguration config) + { + ArgumentNullException.ThrowIfNull(config); + + if (config.Documents is null) + { + throw new ArgumentException("AddTenancyApi callback: config.Documents", nameof(config)); + } + + config.Documents.AddSwaggerEndpoint(); + } + } +} diff --git a/Solutions/Marain.Tenancy.Host.AspNetCore/appsettings.template.json b/Solutions/Marain.Tenancy.Host.AspNetCore/appsettings.template.json new file mode 100644 index 00000000..82efc507 --- /dev/null +++ b/Solutions/Marain.Tenancy.Host.AspNetCore/appsettings.template.json @@ -0,0 +1,15 @@ +// Rename to appsettings.development.json and remove this line. +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "TenantCloudBlobContainerFactoryOptions:AzureServicesAuthConnectionString": "", + "TenantCacheConfiguration:GetTenantResponseCacheControlHeaderValue": "max-age=300", + "RootBlobStorageConfiguration": { + "ConnectionStringPlainText": "DefaultEndpointsProtocol=https;AccountName=;AccountKey=;EndpointSuffix=core.windows.net" + } +} \ No newline at end of file diff --git a/Solutions/Marain.Tenancy.Host.Functions/Marain/Tenancy/Functions/TenancyHost.cs b/Solutions/Marain.Tenancy.Host.Functions/Marain/Tenancy/Functions/TenancyHost.cs index 94e06c69..ee842729 100644 --- a/Solutions/Marain.Tenancy.Host.Functions/Marain/Tenancy/Functions/TenancyHost.cs +++ b/Solutions/Marain.Tenancy.Host.Functions/Marain/Tenancy/Functions/TenancyHost.cs @@ -23,8 +23,7 @@ public class TenancyHost /// Initializes a new instance of the class. /// /// The OpenApi host. - public TenancyHost( - IOpenApiHost host) + public TenancyHost(IOpenApiHost host) { this.host = host; } diff --git a/Solutions/Marain.Tenancy.sln b/Solutions/Marain.Tenancy.sln index f0f82dd2..c95befb6 100644 --- a/Solutions/Marain.Tenancy.sln +++ b/Solutions/Marain.Tenancy.sln @@ -31,6 +31,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Marain.Tenancy.Storage.Azur EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Marain.Tenancy.Storage.Azure.BlobStorage.Specs", "Marain.Tenancy.Storage.Azure.BlobStorage.Specs\Marain.Tenancy.Storage.Azure.BlobStorage.Specs.csproj", "{B9D61A6F-9330-4590-98F2-3A033C96CD2E}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Marain.Tenancy.Host.AspNetCore", "Marain.Tenancy.Host.AspNetCore\Marain.Tenancy.Host.AspNetCore.csproj", "{039CF3D2-264A-4073-8CA0-612186FF809A}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "Docker.Compose\docker-compose.dcproj", "{E6EF3336-38D7-43AE-9840-6359F20DB9C8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -77,6 +81,14 @@ Global {B9D61A6F-9330-4590-98F2-3A033C96CD2E}.Debug|Any CPU.Build.0 = Debug|Any CPU {B9D61A6F-9330-4590-98F2-3A033C96CD2E}.Release|Any CPU.ActiveCfg = Release|Any CPU {B9D61A6F-9330-4590-98F2-3A033C96CD2E}.Release|Any CPU.Build.0 = Release|Any CPU + {039CF3D2-264A-4073-8CA0-612186FF809A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {039CF3D2-264A-4073-8CA0-612186FF809A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {039CF3D2-264A-4073-8CA0-612186FF809A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {039CF3D2-264A-4073-8CA0-612186FF809A}.Release|Any CPU.Build.0 = Release|Any CPU + {E6EF3336-38D7-43AE-9840-6359F20DB9C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E6EF3336-38D7-43AE-9840-6359F20DB9C8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E6EF3336-38D7-43AE-9840-6359F20DB9C8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E6EF3336-38D7-43AE-9840-6359F20DB9C8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE