// <summary> // Returns a lambda function that checks if an incoming request object's url path // is prepended by a string // </summary> // <param name="relativeUrl"> // String keyword with which incoming request's path is matched // </param> // <param name="httpContext"> // The HttpContext object of an incoming request // </param> private void OnShutdown() { KuduWebUtil.TraceShutdown(_webAppRuntimeEnvironment, _noContextDeploymentsSettingsManager); // Cleaning up deployment locks Console.WriteLine(@"Removing Deployment Locks"); FileSystemHelpers.DeleteDirectorySafe("/home/site/locks/deployment"); Console.WriteLine(@"Shutting Down!"); }
internal static void AddDeploymentServices(this IServiceCollection services, IEnvironment environment) { services.AddScoped <ISettings>(sp => new XmlSettings.Settings(KuduWebUtil.GetSettingsPath(environment))); services.AddScoped <IDeploymentSettingsManager, DeploymentSettingsManager>(); services.AddScoped <IDeploymentStatusManager, DeploymentStatusManager>(); services.AddScoped <ISiteBuilderFactory, SiteBuilderFactory>(); services.AddScoped <IWebHooksManager, WebHooksManager>(); }
internal static void AddLogStreamService(this IServiceCollection services, IEnvironment environment, ITraceFactory traceFactory) { var logStreamManagerLock = KuduWebUtil.GetNamedLocks(traceFactory, environment)["hooks"]; services.AddTransient(sp => new LogStreamManager(Path.Combine(environment.RootPath, Constants.LogFilesPath), sp.GetRequiredService <IEnvironment>(), sp.GetRequiredService <IDeploymentSettingsManager>(), sp.GetRequiredService <ITracer>(), logStreamManagerLock)); }
internal static void AddGitServer(this IServiceCollection services, IOperationLock deploymentLock) { services.AddTransient <IDeploymentEnvironment, DeploymentEnvironment>(); services.AddScoped <IGitServer>(sp => new GitExeServer( sp.GetRequiredService <IEnvironment>(), deploymentLock, KuduWebUtil.GetRequestTraceFile(sp), sp.GetRequiredService <IRepositoryFactory>(), sp.GetRequiredService <IDeploymentEnvironment>(), sp.GetRequiredService <IDeploymentSettingsManager>(), sp.GetRequiredService <ITraceFactory>())); }
/// <summary> /// This method gets called by the runtime. It is used to add services /// to the container. It uses the Extension pattern. /// </summary> /// <todo> /// CORE TODO Remove initializing contextAccessor : See if over time we can refactor away the need for this? /// It's kind of a quick hack/compatibility shim. Ideally you want to get the request context only from where /// it's specifically provided to you (Request.HttpContext in a controller, or as an Invoke() parameter in /// a middleware) and pass it wherever its needed. /// </todo> public void ConfigureServices(IServiceCollection services) { Console.WriteLine(@"Configure Services : " + DateTime.Now.ToString("hh.mm.ss.ffffff")); services.Configure <FormOptions>(options => { options.MultipartBodyLengthLimit = 52428800; options.ValueCountLimit = 500000; options.KeyLengthLimit = 500000; }); services.AddRouteAnalyzer(); // Kudu.Services contains all the Controllers var kuduServicesAssembly = Assembly.Load("Kudu.Services"); services.AddMvcCore() .AddRazorPages() .AddAuthorization() .AddJsonFormatters() .AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver()) .AddApplicationPart(kuduServicesAssembly).AddControllersAsServices() .AddApiExplorer(); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new Info { Title = "Kudu API Docs" }); // Setting the comments path for the Swagger JSON and UI. var xmlFile = $"Kudu.Services.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); c.IncludeXmlComments(xmlPath); }); services.AddGZipCompression(); services.AddDirectoryBrowser(); services.AddDataProtection(); services.AddLogging( builder => { builder.AddFilter("Microsoft", LogLevel.Warning) .AddFilter("System", LogLevel.Warning) .AddConsole(); }); services.AddSingleton <IHttpContextAccessor>(new HttpContextAccessor()); KuduWebUtil.EnsureHomeEnvironmentVariable(); KuduWebUtil.EnsureSiteBitnessEnvironmentVariable(); IEnvironment environment = KuduWebUtil.GetEnvironment(_hostingEnvironment); _webAppRuntimeEnvironment = environment; KuduWebUtil.EnsureDotNetCoreEnvironmentVariable(environment); // CORE TODO Check this // fix up invalid /home/site/deployments/settings.xml KuduWebUtil.EnsureValidDeploymentXmlSettings(environment); // Add various folders that never change to the process path. All child processes will inherit this KuduWebUtil.PrependFoldersToPath(environment); // General services.AddSingleton <IServerConfiguration>(ServerConfiguration); // CORE TODO Looks like this doesn't ever actually do anything, can refactor out? services.AddSingleton <IBuildPropertyProvider>(new BuildPropertyProvider()); _noContextDeploymentsSettingsManager = new DeploymentSettingsManager(new XmlSettings.Settings(KuduWebUtil.GetSettingsPath(environment))); TraceServices.TraceLevel = _noContextDeploymentsSettingsManager.GetTraceLevel(); // Per request environment services.AddScoped(sp => KuduWebUtil.GetEnvironment(_hostingEnvironment, sp.GetRequiredService <IDeploymentSettingsManager>())); services.AddDeploymentServices(environment); /* * CORE TODO Refactor ITracerFactory/ITracer/GetTracer()/ * ILogger needs serious refactoring: * - Names should be changed to make it clearer that ILogger is for deployment * logging and ITracer and friends are for Kudu tracing * - ILogger is a first-class citizen in .NET core and has it's own meaning. We should be using it * where appropriate (and not name-colliding with it) * - ITracer vs. ITraceFactory is redundant and confusing. * - TraceServices only serves to confuse stuff now that we're avoiding */ Func <IServiceProvider, ITracer> resolveTracer = KuduWebUtil.GetTracer; ITracer CreateTracerThunk() => resolveTracer(services.BuildServiceProvider()); // First try to use the current request profiler if any, otherwise create a new one var traceFactory = new TracerFactory(() => { var sp = services.BuildServiceProvider(); var context = sp.GetRequiredService <IHttpContextAccessor>().HttpContext; return(TraceServices.GetRequestTracer(context) ?? resolveTracer(sp)); }); services.AddScoped <ITracer>(sp => { var context = sp.GetRequiredService <IHttpContextAccessor>().HttpContext; return(TraceServices.GetRequestTracer(context) ?? NullTracer.Instance); }); services.AddSingleton <ITraceFactory>(traceFactory); TraceServices.SetTraceFactory(CreateTracerThunk); services.AddSingleton <IDictionary <string, IOperationLock> >( KuduWebUtil.GetNamedLocks(traceFactory, environment)); // CORE TODO ShutdownDetector, used by LogStreamManager. //var shutdownDetector = new ShutdownDetector(); //shutdownDetector.Initialize() var noContextTraceFactory = new TracerFactory(() => KuduWebUtil.GetTracerWithoutContext(environment, _noContextDeploymentsSettingsManager)); services.AddTransient <IAnalytics>(sp => new Analytics(sp.GetRequiredService <IDeploymentSettingsManager>(), sp.GetRequiredService <IServerConfiguration>(), noContextTraceFactory)); // CORE TODO // Trace shutdown event // Cannot use shutdownDetector.Token.Register because of race condition // with NinjectServices.Stop via WebActivator.ApplicationShutdownMethodAttribute // Shutdown += () => TraceShutdown(environment, noContextDeploymentsSettingsManager); // LogStream service services.AddLogStreamService(_webAppRuntimeEnvironment, traceFactory); // Deployment Service services.AddWebJobsDependencies(); services.AddScoped <ILogger>(KuduWebUtil.GetDeploymentLogger); services.AddScoped <IDeploymentManager, DeploymentManager>(); services.AddScoped <IFetchDeploymentManager, FetchDeploymentManager>(); services.AddScoped <ISSHKeyManager, SSHKeyManager>(); services.AddScoped <IRepositoryFactory>( sp => KuduWebUtil.GetDeploymentLock(traceFactory, environment).RepositoryFactory = new RepositoryFactory( sp.GetRequiredService <IEnvironment>(), sp.GetRequiredService <IDeploymentSettingsManager>(), sp.GetRequiredService <ITraceFactory>())); services.AddScoped <IApplicationLogsReader, ApplicationLogsReader>(); // Git server services.AddGitServer(KuduWebUtil.GetDeploymentLock(traceFactory, environment)); // Git Servicehook Parsers services.AddGitServiceHookParsers(); services.AddScoped <ICommandExecutor, CommandExecutor>(); // KuduWebUtil.MigrateSite(environment, noContextDeploymentsSettingsManager); // RemoveOldTracePath(environment); // RemoveTempFileFromUserDrive(environment); // CORE TODO Windows Fix: Temporary fix for https://github.com/npm/npm/issues/5905 //EnsureNpmGlobalDirectory(); //EnsureUserProfileDirectory(); //// Skip SSL Certificate Validate //if (Environment.SkipSslValidation) //{ // ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; //} //// Make sure webpages:Enabled is true. Even though we set it in web.config, it could be overwritten by //// an Azure AppSetting that's supposed to be for the site only but incidently affects Kudu as well. ConfigurationManager.AppSettings["webpages:Enabled"] = "true"; //// Kudu does not rely owin:appStartup. This is to avoid Azure AppSetting if set. if (ConfigurationManager.AppSettings?["owin:appStartup"] != null) { // Set the appSetting to null since we cannot use AppSettings.Remove(key) (ReadOnly exception!) ConfigurationManager.AppSettings["owin:appStartup"] = null; } //RegisterRoutes(kernel, RouteTable.Routes); //// Register the default hubs route: ~/signalr //GlobalHost.DependencyResolver = new SignalRNinjectDependencyResolver(kernel); //GlobalConfiguration.Configuration.Filters.Add( // new TraceDeprecatedActionAttribute( // kernel.Get<IAnalytics>(), // kernel.Get<ITraceFactory>())); //GlobalConfiguration.Configuration.Filters.Add(new EnsureRequestIdHandlerAttribute()); //FileTarget target = LogManager.Configuration.FindTargetByName("file") as FileTarget; //String logfile = _webAppRuntimeEnvironment.LogFilesPath + "/.txt"; //target.FileName = logfile; }
public void Configure(IApplicationBuilder app, IApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory) { Console.WriteLine(@"Configure : " + DateTime.Now.ToString("hh.mm.ss.ffffff")); loggerFactory.AddEventSourceLogger(); KuduWebUtil.MigrateToNetCorePatch(_webAppRuntimeEnvironment); if (_hostingEnvironment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); } var webSocketOptions = new WebSocketOptions() { KeepAliveInterval = TimeSpan.FromSeconds(15) }; app.UseWebSockets(webSocketOptions); var containsRelativePath = new Func <HttpContext, bool>(i => i.Request.Path.Value.StartsWith("/Default", StringComparison.OrdinalIgnoreCase)); app.MapWhen(containsRelativePath, application => application.Run(async context => { await context.Response.WriteAsync("Kestrel Running"); })); var containsRelativePath2 = new Func <HttpContext, bool>(i => i.Request.Path.Value.StartsWith("/info", StringComparison.OrdinalIgnoreCase)); app.MapWhen(containsRelativePath2, application => application.Run(async context => { await context.Response.WriteAsync("{\"Version\":\"" + Constants.KuduBuild + "\"}"); })); app.UseResponseCompression(); var containsRelativePath3 = new Func <HttpContext, bool>(i => i.Request.Path.Value.StartsWith("/AppServiceTunnel/Tunnel.ashx", StringComparison.OrdinalIgnoreCase)); app.MapWhen(containsRelativePath3, builder => builder.UseMiddleware <DebugExtensionMiddleware>()); app.UseTraceMiddleware(); applicationLifetime.ApplicationStopping.Register(OnShutdown); app.UseStaticFiles(); ProxyRequestIfRelativeUrlMatches(@"/webssh", "http", "127.0.0.1", "3000", app); var configuration = app.ApplicationServices.GetRequiredService <IServerConfiguration>(); // CORE TODO any equivalent for this? Needed? //var configuration = kernel.Get<IServerConfiguration>(); //GlobalConfiguration.Configuration.Formatters.Clear(); //GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always; var jsonFormatter = new JsonMediaTypeFormatter(); // CORE TODO concept of "deprecation" in routes for traces, Do we need this for linux ? // Push url foreach (var url in new[] { "/git-receive-pack", $"/{configuration.GitServerRoot}/git-receive-pack" }) { app.Map(url, appBranch => appBranch.RunReceivePackHandler()); } // Fetch hook app.Map("/deploy", appBranch => appBranch.RunFetchHandler()); // Log streaming app.Map("/api/logstream", appBranch => appBranch.RunLogStreamHandler()); // Clone url foreach (var url in new[] { "/git-upload-pack", $"/{configuration.GitServerRoot}/git-upload-pack" }) { app.Map(url, appBranch => appBranch.RunUploadPackHandler()); } // Custom GIT repositories, which can be served from any directory that has a git repo foreach (var url in new[] { "/git-custom-repository", "/git/{*path}" }) { app.Map(url, appBranch => appBranch.RunCustomGitRepositoryHandler()); } // Sets up the file server to web app's wwwroot KuduWebUtil.SetupFileServer(app, _webAppRuntimeEnvironment.WebRootPath, "/wwwroot"); // Sets up the file server to LogFiles KuduWebUtil.SetupFileServer(app, Path.Combine(_webAppRuntimeEnvironment.LogFilesPath, "kudu", "deployment"), "/deploymentlogs"); app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "Kudu API Docs"); }); app.UseMvc(routes => { Console.WriteLine(@"Setting Up Routes : " + DateTime.Now.ToString("hh.mm.ss.ffffff")); routes.MapRouteAnalyzer("/routes"); // Add routes.MapRoute( name: "default", template: "{controller}/{action=Index}/{id?}"); // Git Service routes.MapRoute("git-info-refs-root", "info/refs", new { controller = "InfoRefs", action = "Execute" }); routes.MapRoute("git-info-refs", configuration.GitServerRoot + "/info/refs", new { controller = "InfoRefs", action = "Execute" }); // Scm (deployment repository) routes.MapHttpRouteDual("scm-info", "scm/info", new { controller = "LiveScm", action = "GetRepositoryInfo" }); routes.MapHttpRouteDual("scm-clean", "scm/clean", new { controller = "LiveScm", action = "Clean" }); routes.MapHttpRouteDual("scm-delete", "scm", new { controller = "LiveScm", action = "Delete" }, new { verb = new HttpMethodRouteConstraint("DELETE") }); // Scm files editor routes.MapHttpRouteDual("scm-get-files", "scmvfs/{*path}", new { controller = "LiveScmEditor", action = "GetItem" }, new { verb = new HttpMethodRouteConstraint("GET", "HEAD") }); routes.MapHttpRouteDual("scm-put-files", "scmvfs/{*path}", new { controller = "LiveScmEditor", action = "PutItem" }, new { verb = new HttpMethodRouteConstraint("PUT") }); routes.MapHttpRouteDual("scm-delete-files", "scmvfs/{*path}", new { controller = "LiveScmEditor", action = "DeleteItem" }, new { verb = new HttpMethodRouteConstraint("DELETE") }); // Live files editor routes.MapHttpRouteDual("vfs-get-files", "vfs/{*path}", new { controller = "Vfs", action = "GetItem" }, new { verb = new HttpMethodRouteConstraint("GET", "HEAD") }); routes.MapHttpRouteDual("vfs-put-files", "vfs/{*path}", new { controller = "Vfs", action = "PutItem" }, new { verb = new HttpMethodRouteConstraint("PUT") }); routes.MapHttpRouteDual("vfs-delete-files", "vfs/{*path}", new { controller = "Vfs", action = "DeleteItem" }, new { verb = new HttpMethodRouteConstraint("DELETE") }); // Zip file handler routes.MapHttpRouteDual("zip-get-files", "zip/{*path}", new { controller = "Zip", action = "GetItem" }, new { verb = new HttpMethodRouteConstraint("GET", "HEAD") }); routes.MapHttpRouteDual("zip-put-files", "zip/{*path}", new { controller = "Zip", action = "PutItem" }, new { verb = new HttpMethodRouteConstraint("PUT") }); // Zip push deployment routes.MapRoute("zip-push-deploy", "api/zipdeploy", new { controller = "PushDeployment", action = "ZipPushDeploy" }, new { verb = new HttpMethodRouteConstraint("POST") }); routes.MapRoute("zip-war-deploy", "api/wardeploy", new { controller = "PushDeployment", action = "WarPushDeploy" }, new { verb = new HttpMethodRouteConstraint("POST") }); // Live Command Line routes.MapHttpRouteDual("execute-command", "command", new { controller = "Command", action = "ExecuteCommand" }, new { verb = new HttpMethodRouteConstraint("POST") }); // Deployments routes.MapHttpRouteDual("all-deployments", "deployments", new { controller = "Deployment", action = "GetDeployResults" }, new { verb = new HttpMethodRouteConstraint("GET") }); routes.MapHttpRouteDual("one-deployment-get", "deployments/{id}", new { controller = "Deployment", action = "GetResult" }, new { verb = new HttpMethodRouteConstraint("GET") }); routes.MapHttpRouteDual("one-deployment-put", "deployments/{id?}", new { controller = "Deployment", action = "Deploy" }, new { verb = new HttpMethodRouteConstraint("PUT") }); routes.MapHttpRouteDual("one-deployment-delete", "deployments/{id}", new { controller = "Deployment", action = "Delete" }, new { verb = new HttpMethodRouteConstraint("DELETE") }); routes.MapHttpRouteDual("one-deployment-log", "deployments/{id}/log", new { controller = "Deployment", action = "GetLogEntry" }); routes.MapHttpRouteDual("one-deployment-log-details", "deployments/{id}/log/{logId}", new { controller = "Deployment", action = "GetLogEntryDetails" }); // Deployment script routes.MapRoute("get-deployment-script", "api/deploymentscript", new { controller = "Deployment", action = "GetDeploymentScript" }, new { verb = new HttpMethodRouteConstraint("GET") }); // IsDeploying status routes.MapRoute("is-deployment-underway", "api/isdeploying", new { controller = "Deployment", action = "IsDeploying" }, new { verb = new HttpMethodRouteConstraint("GET") }); // SSHKey routes.MapHttpRouteDual("get-sshkey", "api/sshkey", new { controller = "SSHKey", action = "GetPublicKey" }, new { verb = new HttpMethodRouteConstraint("GET") }); routes.MapHttpRouteDual("put-sshkey", "api/sshkey", new { controller = "SSHKey", action = "SetPrivateKey" }, new { verb = new HttpMethodRouteConstraint("PUT") }); routes.MapHttpRouteDual("delete-sshkey", "api/sshkey", new { controller = "SSHKey", action = "DeleteKeyPair" }, new { verb = new HttpMethodRouteConstraint("DELETE") }); // Environment routes.MapHttpRouteDual("get-env", "environment", new { controller = "Environment", action = "Get" }, new { verb = new HttpMethodRouteConstraint("GET") }); // Settings routes.MapHttpRouteDual("set-setting", "settings", new { controller = "Settings", action = "Set" }, new { verb = new HttpMethodRouteConstraint("POST") }); routes.MapHttpRouteDual("get-all-settings", "settings", new { controller = "Settings", action = "GetAll" }, new { verb = new HttpMethodRouteConstraint("GET") }); routes.MapHttpRouteDual("get-setting", "settings/{key}", new { controller = "Settings", action = "Get" }, new { verb = new HttpMethodRouteConstraint("GET") }); routes.MapHttpRouteDual("delete-setting", "settings/{key}", new { controller = "Settings", action = "Delete" }, new { verb = new HttpMethodRouteConstraint("DELETE") }); // Diagnostics routes.MapHttpRouteDual("diagnostics", "dump", new { controller = "Diagnostics", action = "GetLog" }); routes.MapHttpRouteDual("diagnostics-set-setting", "diagnostics/settings", new { controller = "Diagnostics", action = "Set" }, new { verb = new HttpMethodRouteConstraint("POST") }); routes.MapHttpRouteDual("diagnostics-get-all-settings", "diagnostics/settings", new { controller = "Diagnostics", action = "GetAll" }, new { verb = new HttpMethodRouteConstraint("GET") }); routes.MapHttpRouteDual("diagnostics-get-setting", "diagnostics/settings/{key}", new { controller = "Diagnostics", action = "Get" }, new { verb = new HttpMethodRouteConstraint("GET") }); routes.MapHttpRouteDual("diagnostics-delete-setting", "diagnostics/settings/{key}", new { controller = "Diagnostics", action = "Delete" }, new { verb = new HttpMethodRouteConstraint("DELETE") }); // Logs foreach (var url in new[] { "/logstream", "/logstream/{*path}" }) { app.Map(url, appBranch => appBranch.RunLogStreamHandler()); } routes.MapHttpRouteDual("recent-logs", "api/logs/recent", new { controller = "Diagnostics", action = "GetRecentLogs" }, new { verb = new HttpMethodRouteConstraint("GET") }); // Enable these for Linux and Windows Containers. if (!OSDetector.IsOnWindows() || (OSDetector.IsOnWindows() && EnvironmentHelper.IsWindowsContainers())) { routes.MapRoute("current-docker-logs-zip", "api/logs/docker/zip", new { controller = "Diagnostics", action = "GetDockerLogsZip" }, new { verb = new HttpMethodRouteConstraint("GET") }); routes.MapRoute("current-docker-logs", "api/logs/docker", new { controller = "Diagnostics", action = "GetDockerLogs" }, new { verb = new HttpMethodRouteConstraint("GET") }); } var processControllerName = OSDetector.IsOnWindows() ? "Process" : "LinuxProcess"; // Processes routes.MapHttpProcessesRoute("all-processes", "", new { controller = processControllerName, action = "GetAllProcesses" }, new { verb = new HttpMethodRouteConstraint("GET") }); routes.MapHttpProcessesRoute("one-process-get", "/{id}", new { controller = processControllerName, action = "GetProcess" }, new { verb = new HttpMethodRouteConstraint("GET") }); routes.MapHttpProcessesRoute("one-process-delete", "/{id}", new { controller = processControllerName, action = "KillProcess" }, new { verb = new HttpMethodRouteConstraint("DELETE") }); routes.MapHttpProcessesRoute("one-process-dump", "/{id}/dump", new { controller = processControllerName, action = "MiniDump" }, new { verb = new HttpMethodRouteConstraint("GET") }); routes.MapHttpProcessesRoute("start-process-profile", "/{id}/profile/start", new { controller = processControllerName, action = "StartProfileAsync" }, new { verb = new HttpMethodRouteConstraint("POST") }); routes.MapHttpProcessesRoute("stop-process-profile", "/{id}/profile/stop", new { controller = processControllerName, action = "StopProfileAsync" }, new { verb = new HttpMethodRouteConstraint("GET") }); routes.MapHttpProcessesRoute("all-threads", "/{id}/threads", new { controller = processControllerName, action = "GetAllThreads" }, new { verb = new HttpMethodRouteConstraint("GET") }); routes.MapHttpProcessesRoute("one-process-thread", "/{processId}/threads/{threadId}", new { controller = processControllerName, action = "GetThread" }, new { verb = new HttpMethodRouteConstraint("GET") }); routes.MapHttpProcessesRoute("all-modules", "/{id}/modules", new { controller = processControllerName, action = "GetAllModules" }, new { verb = new HttpMethodRouteConstraint("GET") }); routes.MapHttpProcessesRoute("one-process-module", "/{id}/modules/{baseAddress}", new { controller = processControllerName, action = "GetModule" }, new { verb = new HttpMethodRouteConstraint("GET") }); // Runtime routes.MapHttpRouteDual("runtime", "diagnostics/runtime", new { controller = "Runtime", action = "GetRuntimeVersions" }, new { verb = new HttpMethodRouteConstraint("GET") }); // Docker Hook Endpoint if (!OSDetector.IsOnWindows() || (OSDetector.IsOnWindows() && EnvironmentHelper.IsWindowsContainers())) { routes.MapHttpRouteDual("docker", "docker/hook", new { controller = "Docker", action = "ReceiveHook" }, new { verb = new HttpMethodRouteConstraint("POST") }); } // catch all unregistered url to properly handle not found // routes.MapRoute("error-404", "{*path}", new {controller = "Error404", action = "Handle"}); }); Console.WriteLine(@"Exiting Configure : " + DateTime.Now.ToString("hh.mm.ss.ffffff")); }
/// <summary> /// This method gets called by the runtime. It is used to add services /// to the container. It uses the Extension pattern. /// </summary> /// <todo> /// CORE TODO Remove initializing contextAccessor : This is new. See if over time we can refactor away the need for this? /// It's kind of a quick hack/compatibility shim. Ideally you want to get the request context only from where /// it's specifically provided to you (Request.HttpContext in a controller, or as an Invoke() parameter in /// a middleware) and pass it wherever its needed. /// </todo> public void ConfigureServices(IServiceCollection services) { Console.WriteLine("\nConfigure Services : " + DateTime.Now.ToString("hh.mm.ss.ffffff")); services.Configure <FormOptions>(options => { options.MultipartBodyLengthLimit = 52428800; options.ValueCountLimit = 500000; options.KeyLengthLimit = 500000; }); services.AddMvcCore() .AddRazorPages() .AddAuthorization() .AddFormatterMappings() .AddJsonFormatters() .AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver()); services.AddGZipCompression(); services.AddDirectoryBrowser(); services.AddDataProtection(); var contextAccessor = new HttpContextAccessor(); this._httpContextAccessor = contextAccessor; services.AddSingleton <IHttpContextAccessor>(contextAccessor); KuduWebUtil.EnsureHomeEnvironmentVariable(); KuduWebUtil.EnsureSiteBitnessEnvironmentVariable(); IEnvironment environment = KuduWebUtil.GetEnvironment(_hostingEnvironment); _webAppEnvironment = environment; KuduWebUtil.EnsureDotNetCoreEnvironmentVariable(environment); //CORE TODO Check this // fix up invalid /home/site/deployments/settings.xml KuduWebUtil.EnsureValidDeploymentXmlSettings(environment); // Add various folders that never change to the process path. All child processes will inherit this KuduWebUtil.PrependFoldersToPath(environment); // General services.AddSingleton <IServerConfiguration>(serverConfiguration); // CORE TODO Looks like this doesn't ever actually do anything, can refactor out? services.AddSingleton <IBuildPropertyProvider>(new BuildPropertyProvider()); IDeploymentSettingsManager noContextDeploymentsSettingsManager = new DeploymentSettingsManager(new XmlSettings.Settings(KuduWebUtil.GetSettingsPath(environment))); TraceServices.TraceLevel = noContextDeploymentsSettingsManager.GetTraceLevel(); // Per request environment services.AddScoped <IEnvironment>(sp => KuduWebUtil.GetEnvironment(_hostingEnvironment, sp.GetRequiredService <IDeploymentSettingsManager>(), sp.GetRequiredService <IHttpContextAccessor>().HttpContext)); services.AddDeployementServices(environment); /* * CORE TODO all this business around ITracerFactory/ITracer/GetTracer()/ * ILogger needs serious refactoring: * - Names should be changed to make it clearer that ILogger is for deployment * logging and ITracer and friends are for Kudu tracing * - ILogger is a first-class citizen in .NET core and has it's own meaning. We should be using it * where appropriate (and not name-colliding with it) * - ITracer vs. ITraceFactory is redundant and confusing. * - All this stuff with funcs and factories and TraceServices is overcomplicated. * TraceServices only serves to confuse stuff now that we're avoiding */ Func <IServiceProvider, ITracer> resolveTracer = sp => KuduWebUtil.GetTracer(sp); ITracer CreateTracerThunk() => resolveTracer(services.BuildServiceProvider()); // First try to use the current request profiler if any, otherwise create a new one var traceFactory = new TracerFactory(() => { var sp = services.BuildServiceProvider(); var context = sp.GetRequiredService <IHttpContextAccessor>().HttpContext; return(TraceServices.GetRequestTracer(context) ?? resolveTracer(sp)); }); services.AddScoped <ITracer>(sp => { var context = sp.GetRequiredService <IHttpContextAccessor>().HttpContext; return(TraceServices.GetRequestTracer(context) ?? NullTracer.Instance); }); services.AddSingleton <ITraceFactory>(traceFactory); TraceServices.SetTraceFactory(CreateTracerThunk); services.AddSingleton <IDictionary <string, IOperationLock> >(KuduWebUtil.GetNamedLocks(traceFactory, environment)); // CORE TODO ShutdownDetector, used by LogStreamManager. //var shutdownDetector = new ShutdownDetector(); //shutdownDetector.Initialize() var noContextTraceFactory = new TracerFactory(() => KuduWebUtil.GetTracerWithoutContext(environment, noContextDeploymentsSettingsManager)); var etwTraceFactory = new TracerFactory(() => new ETWTracer(string.Empty, string.Empty)); services.AddTransient <IAnalytics>(sp => new Analytics(sp.GetRequiredService <IDeploymentSettingsManager>(), sp.GetRequiredService <IServerConfiguration>(), noContextTraceFactory)); // CORE TODO Trace unhandled exceptions //AppDomain.CurrentDomain.UnhandledException += (sender, args) => //{ // var ex = args.ExceptionObject as Exception; // if (ex != null) // { // kernel.Get<IAnalytics>().UnexpectedException(ex); // } //}; // CORE TODO // Trace shutdown event // Cannot use shutdownDetector.Token.Register because of race condition // with NinjectServices.Stop via WebActivator.ApplicationShutdownMethodAttribute //Shutdown += () => TraceShutdown(environment, noContextDeploymentsSettingsManager); // CORE TODO // LogStream service // The hooks and log stream start endpoint are low traffic end-points. Re-using it to avoid creating another lock var logStreamManagerLock = KuduWebUtil.GetNamedLocks(traceFactory, environment)["hooks"]; //kernel.Bind<LogStreamManager>().ToMethod(context => new LogStreamManager(Path.Combine(environment.RootPath, Constants.LogFilesPath), // context.Kernel.Get<IEnvironment>(), // context.Kernel.Get<IDeploymentSettingsManager>(), // context.Kernel.Get<ITracer>(), // shutdownDetector, // logStreamManagerLock)); services.AddTransient(sp => new LogStreamManager(Path.Combine(environment.RootPath, Constants.LogFilesPath), sp.GetRequiredService <IEnvironment>(), sp.GetRequiredService <IDeploymentSettingsManager>(), sp.GetRequiredService <ITracer>(), logStreamManagerLock)); // CORE TODO Need to implement this, and same comment as in InfoRefsController.cs (not sure why it needs the kernel/iserviceprovider as a // service locator, why does it need "delayed binding"?) //kernel.Bind<CustomGitRepositoryHandler>().ToMethod(context => new CustomGitRepositoryHandler(t => context.Kernel.Get(t))) // .InRequestScope(); // Deployment Service services.AddWebJobsDependencies(); services.AddScoped <ILogger>(sp => KuduWebUtil.GetLogger(sp)); services.AddScoped <IDeploymentManager, DeploymentManager>(); services.AddScoped <IFetchDeploymentManager, FetchDeploymentManager>(); services.AddScoped <ISSHKeyManager, SSHKeyManager>(); services.AddScoped <IRepositoryFactory>(sp => KuduWebUtil.GetDeploymentLock(traceFactory, environment).RepositoryFactory = new RepositoryFactory( sp.GetRequiredService <IEnvironment>(), sp.GetRequiredService <IDeploymentSettingsManager>(), sp.GetRequiredService <ITraceFactory>())); // CORE NOTE This was previously wired up in Ninject with .InSingletonScope. I'm not sure how that worked, // since it depends on an IEnvironment, which was set up with .PerRequestScope. I have made this per request. services.AddScoped <IApplicationLogsReader, ApplicationLogsReader>(); // Git server services.AddGitServer(KuduWebUtil.GetDeploymentLock(traceFactory, environment)); // Git Servicehook Parsers services.AddGitServiceHookParsers(); // CORE TODO // SiteExtensions //kernel.Bind<ISiteExtensionManager>().To<SiteExtensionManager>().InRequestScope(); // CORE TODO // Functions //kernel.Bind<IFunctionManager>().To<FunctionManager>().InRequestScope(); services.AddScoped <ICommandExecutor, CommandExecutor>(); // CORE TODO This stuff should probably go in a separate method // (they don't really fit into "ConfigureServices"), and much of it is probably no longer needed //MigrateSite(environment, noContextDeploymentsSettingsManager); //RemoveOldTracePath(environment); //RemoveTempFileFromUserDrive(environment); //// Temporary fix for https://github.com/npm/npm/issues/5905 //EnsureNpmGlobalDirectory(); //EnsureUserProfileDirectory(); //// Skip SSL Certificate Validate //if (Kudu.Core.Environment.SkipSslValidation) //{ // ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; //} //// Make sure webpages:Enabled is true. Even though we set it in web.config, it could be overwritten by //// an Azure AppSetting that's supposed to be for the site only but incidently affects Kudu as well. ConfigurationManager.AppSettings["webpages:Enabled"] = "true"; //// Kudu does not rely owin:appStartup. This is to avoid Azure AppSetting if set. if (ConfigurationManager.AppSettings?["owin:appStartup"] != null) { // Set the appSetting to null since we cannot use AppSettings.Remove(key) (ReadOnly exception!) ConfigurationManager.AppSettings["owin:appStartup"] = null; } //RegisterRoutes(kernel, RouteTable.Routes); //// Register the default hubs route: ~/signalr //GlobalHost.DependencyResolver = new SignalRNinjectDependencyResolver(kernel); //GlobalConfiguration.Configuration.Filters.Add( // new TraceDeprecatedActionAttribute( // kernel.Get<IAnalytics>(), // kernel.Get<ITraceFactory>())); //GlobalConfiguration.Configuration.Filters.Add(new EnsureRequestIdHandlerAttribute()); //FileTarget target = LogManager.Configuration.FindTargetByName("file") as FileTarget; //String logfile = _webAppEnvironment.LogFilesPath + "/.txt"; //target.FileName = logfile; }