// key goal is to create background tracer that is independent of request. public static void PerformBackgroundDeployment(DeploymentInfo deployInfo, IEnvironment environment, IDeploymentSettingsManager settings, TraceLevel traceLevel, Uri uri) { var tracer = traceLevel <= TraceLevel.Off ? NullTracer.Instance : new XmlTracer(environment.TracePath, traceLevel); var traceFactory = new TracerFactory(() => tracer); var backgroundTrace = tracer.Step(XmlTracer.BackgroundTrace, new Dictionary <string, string> { { "url", uri.AbsolutePath }, { "method", "POST" } }); Task.Run(() => { try { // lock related string lockPath = Path.Combine(environment.SiteRootPath, Constants.LockPath); string deploymentLockPath = Path.Combine(lockPath, Constants.DeploymentLockFile); string statusLockPath = Path.Combine(lockPath, Constants.StatusLockFile); string hooksLockPath = Path.Combine(lockPath, Constants.HooksLockFile); var statusLock = new LockFile(statusLockPath, traceFactory); var hooksLock = new LockFile(hooksLockPath, traceFactory); var deploymentLock = new DeploymentLockFile(deploymentLockPath, traceFactory); var analytics = new Analytics(settings, new ServerConfiguration(), traceFactory); var deploymentStatusManager = new DeploymentStatusManager(environment, analytics, statusLock); var repositoryFactory = new RepositoryFactory(environment, settings, traceFactory); var siteBuilderFactory = new SiteBuilderFactory(new BuildPropertyProvider(), environment); var webHooksManager = new WebHooksManager(tracer, environment, hooksLock); var deploymentManager = new DeploymentManager(siteBuilderFactory, environment, traceFactory, analytics, settings, deploymentStatusManager, deploymentLock, NullLogger.Instance, webHooksManager); var fetchHandler = new FetchHandler(tracer, deploymentManager, settings, deploymentStatusManager, deploymentLock, environment, null, repositoryFactory, null); // Perform deployment var acquired = deploymentLock.TryLockOperation(() => { fetchHandler.PerformDeployment(deployInfo).Wait(); }, TimeSpan.Zero); if (!acquired) { using (tracer.Step("Update pending deployment marker file")) { // REVIEW: This makes the assumption that the repository url is the same. // If it isn't the result would be buggy either way. FileSystemHelpers.SetLastWriteTimeUtc(fetchHandler._markerFilePath, DateTime.UtcNow); } } } catch (Exception ex) { tracer.TraceError(ex); } finally { backgroundTrace.Dispose(); } }); }
// key goal is to create background tracer that is independent of request. public static async Task <bool> PerformBackgroundDeployment( DeploymentInfoBase deployInfo, IEnvironment environment, IDeploymentSettingsManager settings, TraceLevel traceLevel, Uri uri, bool waitForTempDeploymentCreation) { var tracer = traceLevel <= TraceLevel.Off ? NullTracer.Instance : new CascadeTracer(new XmlTracer(environment.TracePath, traceLevel), new ETWTracer(environment.RequestId, "POST")); var traceFactory = new TracerFactory(() => tracer); var backgroundTrace = tracer.Step(XmlTracer.BackgroundTrace, new Dictionary <string, string> { { "url", uri.AbsolutePath }, { "method", "POST" } }); // For waiting on creation of temp deployment var tempDeploymentCreatedTcs = new TaskCompletionSource <object>(); // For determining whether or not we failed to create the deployment due to lock contention. // Needed for deployments where deferred deployment is not allowed. Will be set to false if // lock contention occurs and AllowDeferredDeployment is false, otherwise true. var deploymentWillOccurTcs = new TaskCompletionSource <bool>(); // This task will be let out of scope intentionally var deploymentTask = Task.Run(() => { try { // lock related string lockPath = Path.Combine(environment.SiteRootPath, Constants.LockPath); string deploymentLockPath = Path.Combine(lockPath, Constants.DeploymentLockFile); string statusLockPath = Path.Combine(lockPath, Constants.StatusLockFile); string hooksLockPath = Path.Combine(lockPath, Constants.HooksLockFile); var statusLock = new LockFile(statusLockPath, traceFactory, traceLock: false); var hooksLock = new LockFile(hooksLockPath, traceFactory); var deploymentLock = new DeploymentLockFile(deploymentLockPath, traceFactory); var analytics = new Analytics(settings, new ServerConfiguration(), traceFactory); var deploymentStatusManager = new DeploymentStatusManager(environment, analytics, statusLock); var siteBuilderFactory = new SiteBuilderFactory(new BuildPropertyProvider(), environment); var webHooksManager = new WebHooksManager(tracer, environment, hooksLock); var deploymentManager = new DeploymentManager(siteBuilderFactory, environment, traceFactory, analytics, settings, deploymentStatusManager, deploymentLock, NullLogger.Instance, webHooksManager); var fetchDeploymentManager = new FetchDeploymentManager(settings, environment, tracer, deploymentLock, deploymentManager, deploymentStatusManager); IDisposable tempDeployment = null; try { // Perform deployment deploymentLock.LockOperation(() => { deploymentWillOccurTcs.TrySetResult(true); ChangeSet tempChangeSet = null; if (waitForTempDeploymentCreation) { // create temporary deployment before the actual deployment item started // this allows portal ui to readily display on-going deployment (not having to wait for fetch to complete). // in addition, it captures any failure that may occur before the actual deployment item started tempDeployment = deploymentManager.CreateTemporaryDeployment( Resources.ReceivingChanges, out tempChangeSet, deployInfo.TargetChangeset, deployInfo.Deployer); tempDeploymentCreatedTcs.TrySetResult(null); } fetchDeploymentManager.PerformDeployment(deployInfo, tempDeployment, tempChangeSet).Wait(); }, "Performing continuous deployment", TimeSpan.Zero); } catch (LockOperationException) { if (tempDeployment != null) { OperationManager.SafeExecute(() => tempDeployment.Dispose()); } if (deployInfo.AllowDeferredDeployment) { deploymentWillOccurTcs.TrySetResult(true); using (tracer.Step("Update pending deployment marker file")) { // REVIEW: This makes the assumption that the repository url is the same. // If it isn't the result would be buggy either way. FileSystemHelpers.SetLastWriteTimeUtc(fetchDeploymentManager._markerFilePath, DateTime.UtcNow); } } } } catch (Exception ex) { tracer.TraceError(ex); } finally { // Will no-op if already set deploymentWillOccurTcs.TrySetResult(false); backgroundTrace.Dispose(); } }); #pragma warning disable 4014 // Run on BG task (Task.Run) to avoid ASP.NET Request thread terminated with request completion and // it doesn't get chance to clean up the pending marker. Task.Run(() => PostDeploymentHelper.TrackPendingOperation(deploymentTask, TimeSpan.Zero)); #pragma warning restore 4014 // When the frontend/ARM calls /deploy with isAsync=true, it starts polling // the deployment status immediately, so it's important that the temp deployment // is created before we return. if (waitForTempDeploymentCreation) { // deploymentTask may return without creating the temp deployment (lock contention, // other exception), in which case just continue. await Task.WhenAny(tempDeploymentCreatedTcs.Task, deploymentTask); } // If deferred deployment is not permitted, we need to know whether or not the deployment was // successfully requested. Otherwise, to preserve existing behavior, we assume it was. if (!deployInfo.AllowDeferredDeployment) { return(await deploymentWillOccurTcs.Task); } else { return(true); } }
private static int Main(string[] args) { // Turn flag on in app.config to wait for debugger on launch if (ConfigurationManager.AppSettings["WaitForDebuggerOnStart"] == "true") { while (!Debugger.IsAttached) { System.Threading.Thread.Sleep(100); } } if (System.Environment.GetEnvironmentVariable(SettingsKeys.DisableDeploymentOnPush) == "1") { return(0); } if (args.Length < 2) { System.Console.WriteLine("Usage: kudu.exe appRoot wapTargets [deployer]"); return(1); } // The post receive hook launches the exe from sh and intereprets newline differently. // This fixes very wacky issues with how the output shows up in the console on push System.Console.Error.NewLine = "\n"; System.Console.Out.NewLine = "\n"; System.Environment.SetEnvironmentVariable("GIT_DIR", null, System.EnvironmentVariableTarget.Process); // Skip SSL Certificate Validate OperationClient.SkipSslValidationIfNeeded(); string appRoot = args[0]; string wapTargets = args[1]; string deployer = args.Length == 2 ? null : args[2]; IEnvironment env = GetEnvironment(appRoot); ISettings settings = new XmlSettings.Settings(GetSettingsPath(env)); IDeploymentSettingsManager settingsManager = new DeploymentSettingsManager(settings); // Adjust repo path env.RepositoryPath = Path.Combine(env.SiteRootPath, settingsManager.GetRepositoryPath()); // Setup the trace TraceLevel level = settingsManager.GetTraceLevel(); ITracer tracer = GetTracer(env, level); ITraceFactory traceFactory = new TracerFactory(() => tracer); // Calculate the lock path string lockPath = Path.Combine(env.SiteRootPath, Constants.LockPath); string deploymentLockPath = Path.Combine(lockPath, Constants.DeploymentLockFile); string statusLockPath = Path.Combine(lockPath, Constants.StatusLockFile); string hooksLockPath = Path.Combine(lockPath, Constants.HooksLockFile); IOperationLock deploymentLock = new LockFile(deploymentLockPath, traceFactory); IOperationLock statusLock = new LockFile(statusLockPath, traceFactory); IOperationLock hooksLock = new LockFile(hooksLockPath, traceFactory); IBuildPropertyProvider buildPropertyProvider = new BuildPropertyProvider(); ISiteBuilderFactory builderFactory = new SiteBuilderFactory(buildPropertyProvider, env); IRepository gitRepository; if (settingsManager.UseLibGit2SharpRepository()) { gitRepository = new LibGit2SharpRepository(env, settingsManager, traceFactory); } else { gitRepository = new GitExeRepository(env, settingsManager, traceFactory); } IServerConfiguration serverConfiguration = new ServerConfiguration(); IAnalytics analytics = new Analytics(settingsManager, serverConfiguration, traceFactory); IWebHooksManager hooksManager = new WebHooksManager(tracer, env, hooksLock); IDeploymentStatusManager deploymentStatusManager = new DeploymentStatusManager(env, analytics, statusLock); IAutoSwapHandler autoSwapHander = new AutoSwapHandler(deploymentStatusManager, env, settingsManager, traceFactory); var functionManager = new FunctionManager(env, traceFactory); var logger = new ConsoleLogger(); IDeploymentManager deploymentManager = new DeploymentManager(builderFactory, env, traceFactory, analytics, settingsManager, deploymentStatusManager, deploymentLock, GetLogger(env, level, logger), hooksManager, autoSwapHander, functionManager); var step = tracer.Step(XmlTracer.ExecutingExternalProcessTrace, new Dictionary <string, string> { { "type", "process" }, { "path", "kudu.exe" }, { "arguments", appRoot + " " + wapTargets } }); using (step) { try { deploymentManager.DeployAsync(gitRepository, changeSet: null, deployer: deployer, clean: false) .Wait(); } catch (Exception e) { tracer.TraceError(e); System.Console.Error.WriteLine(e.GetBaseException().Message); System.Console.Error.WriteLine(Resources.Log_DeploymentError); return(1); } } if (logger.HasErrors) { System.Console.Error.WriteLine(Resources.Log_DeploymentError); return(1); } return(0); }
private static int PerformDeploy( string appRoot, string wapTargets, string deployer, string lockPath, IEnvironment env, IDeploymentSettingsManager settingsManager, TraceLevel level, ITracer tracer, ITraceFactory traceFactory, IOperationLock deploymentLock) { System.Environment.SetEnvironmentVariable("GIT_DIR", null, System.EnvironmentVariableTarget.Process); // Skip SSL Certificate Validate if (System.Environment.GetEnvironmentVariable(SettingsKeys.SkipSslValidation) == "1") { ServicePointManager.ServerCertificateValidationCallback = delegate { return(true); }; } // Adjust repo path env.RepositoryPath = Path.Combine(env.SiteRootPath, settingsManager.GetRepositoryPath()); string statusLockPath = Path.Combine(lockPath, Constants.StatusLockFile); string hooksLockPath = Path.Combine(lockPath, Constants.HooksLockFile); IOperationLock statusLock = new LockFile(statusLockPath, traceFactory); IOperationLock hooksLock = new LockFile(hooksLockPath, traceFactory); IBuildPropertyProvider buildPropertyProvider = new BuildPropertyProvider(); ISiteBuilderFactory builderFactory = new SiteBuilderFactory(buildPropertyProvider, env); var logger = new ConsoleLogger(); IRepository gitRepository; if (settingsManager.UseLibGit2SharpRepository()) { gitRepository = new LibGit2SharpRepository(env, settingsManager, traceFactory); } else { gitRepository = new GitExeRepository(env, settingsManager, traceFactory); } IServerConfiguration serverConfiguration = new ServerConfiguration(); IAnalytics analytics = new Analytics(settingsManager, serverConfiguration, traceFactory); IWebHooksManager hooksManager = new WebHooksManager(tracer, env, hooksLock); IDeploymentStatusManager deploymentStatusManager = new DeploymentStatusManager(env, analytics, statusLock); IDeploymentManager deploymentManager = new DeploymentManager(builderFactory, env, traceFactory, analytics, settingsManager, deploymentStatusManager, deploymentLock, GetLogger(env, level, logger), hooksManager); var step = tracer.Step(XmlTracer.ExecutingExternalProcessTrace, new Dictionary <string, string> { { "type", "process" }, { "path", "kudu.exe" }, { "arguments", appRoot + " " + wapTargets } }); using (step) { try { // although the api is called DeployAsync, most expensive works are done synchronously. // need to launch separate task to go async explicitly (consistent with FetchDeploymentManager) var deploymentTask = Task.Run(async() => await deploymentManager.DeployAsync(gitRepository, changeSet: null, deployer: deployer, clean: false)); #pragma warning disable 4014 // Track pending task PostDeploymentHelper.TrackPendingOperation(deploymentTask, TimeSpan.Zero); #pragma warning restore 4014 deploymentTask.Wait(); if (PostDeploymentHelper.IsAutoSwapEnabled()) { string branch = settingsManager.GetBranch(); ChangeSet changeSet = gitRepository.GetChangeSet(branch); IDeploymentStatusFile statusFile = deploymentStatusManager.Open(changeSet.Id); if (statusFile != null && statusFile.Status == DeployStatus.Success) { PostDeploymentHelper.PerformAutoSwap(env.RequestId, new PostDeploymentTraceListener(tracer, deploymentManager.GetLogger(changeSet.Id))).Wait(); } } } catch (Exception e) { tracer.TraceError(e); System.Console.Error.WriteLine(e.GetBaseException().Message); System.Console.Error.WriteLine(Resources.Log_DeploymentError); return(1); } } if (logger.HasErrors) { System.Console.Error.WriteLine(Resources.Log_DeploymentError); return(1); } return(0); }
// key goal is to create background tracer that is independent of request. public static async Task PerformBackgroundDeployment( DeploymentInfo deployInfo, IEnvironment environment, IDeploymentSettingsManager settings, TraceLevel traceLevel, Uri uri, bool waitForTempDeploymentCreation) { var tracer = traceLevel <= TraceLevel.Off ? NullTracer.Instance : new CascadeTracer(new XmlTracer(environment.TracePath, traceLevel), new ETWTracer(environment.RequestId, "POST")); var traceFactory = new TracerFactory(() => tracer); var backgroundTrace = tracer.Step(XmlTracer.BackgroundTrace, new Dictionary <string, string> { { "url", uri.AbsolutePath }, { "method", "POST" } }); // For monitoring creation of temp deployment var tcs = new TaskCompletionSource <object>(); // This task will be let out of scope intentionally var deploymentTask = Task.Run(() => { try { // lock related string lockPath = Path.Combine(environment.SiteRootPath, Constants.LockPath); string deploymentLockPath = Path.Combine(lockPath, Constants.DeploymentLockFile); string statusLockPath = Path.Combine(lockPath, Constants.StatusLockFile); string hooksLockPath = Path.Combine(lockPath, Constants.HooksLockFile); var statusLock = new LockFile(statusLockPath, traceFactory); var hooksLock = new LockFile(hooksLockPath, traceFactory); var deploymentLock = new DeploymentLockFile(deploymentLockPath, traceFactory); var analytics = new Analytics(settings, new ServerConfiguration(), traceFactory); var deploymentStatusManager = new DeploymentStatusManager(environment, analytics, statusLock); var repositoryFactory = new RepositoryFactory(environment, settings, traceFactory); var siteBuilderFactory = new SiteBuilderFactory(new BuildPropertyProvider(), environment); var webHooksManager = new WebHooksManager(tracer, environment, hooksLock); var deploymentManager = new DeploymentManager(siteBuilderFactory, environment, traceFactory, analytics, settings, deploymentStatusManager, deploymentLock, NullLogger.Instance, webHooksManager); var fetchHandler = new FetchHandler(tracer, deploymentManager, settings, deploymentStatusManager, deploymentLock, environment, null, repositoryFactory); IDisposable tempDeployment = null; try { // Perform deployment deploymentLock.LockOperation(() => { ChangeSet tempChangeSet = null; if (waitForTempDeploymentCreation) { // create temporary deployment before the actual deployment item started // this allows portal ui to readily display on-going deployment (not having to wait for fetch to complete). // in addition, it captures any failure that may occur before the actual deployment item started tempDeployment = deploymentManager.CreateTemporaryDeployment( Resources.ReceivingChanges, out tempChangeSet, deployInfo.TargetChangeset, deployInfo.Deployer); tcs.TrySetResult(null); } fetchHandler.PerformDeployment(deployInfo, tempDeployment, tempChangeSet).Wait(); }, "Performing continuous deployment", TimeSpan.Zero); } catch (LockOperationException) { if (tempDeployment != null) { tempDeployment.Dispose(); } using (tracer.Step("Update pending deployment marker file")) { // REVIEW: This makes the assumption that the repository url is the same. // If it isn't the result would be buggy either way. FileSystemHelpers.SetLastWriteTimeUtc(fetchHandler._markerFilePath, DateTime.UtcNow); } } } catch (Exception ex) { tracer.TraceError(ex); } finally { backgroundTrace.Dispose(); } }); // When the frontend/ARM calls /deploy with isAsync=true, it starts polling // the deployment status immediately, so it's important that the temp deployment // is created before we return. if (waitForTempDeploymentCreation) { // If deploymentTask blows up before it creates the temp deployment, // go ahead and return. await Task.WhenAny(tcs.Task, deploymentTask); } }
private static int PerformDeploy( string appRoot, string wapTargets, string deployer, string lockPath, IEnvironment env, IDeploymentSettingsManager settingsManager, TraceLevel level, ITracer tracer, ITraceFactory traceFactory, IOperationLock deploymentLock) { System.Environment.SetEnvironmentVariable("GIT_DIR", null, System.EnvironmentVariableTarget.Process); // Skip SSL Certificate Validate OperationClient.SkipSslValidationIfNeeded(); // Adjust repo path env.RepositoryPath = Path.Combine(env.SiteRootPath, settingsManager.GetRepositoryPath()); string statusLockPath = Path.Combine(lockPath, Constants.StatusLockFile); string hooksLockPath = Path.Combine(lockPath, Constants.HooksLockFile); IOperationLock statusLock = new LockFile(statusLockPath, traceFactory); IOperationLock hooksLock = new LockFile(hooksLockPath, traceFactory); IBuildPropertyProvider buildPropertyProvider = new BuildPropertyProvider(); ISiteBuilderFactory builderFactory = new SiteBuilderFactory(buildPropertyProvider, env); var logger = new ConsoleLogger(); IRepository gitRepository; if (settingsManager.UseLibGit2SharpRepository()) { gitRepository = new LibGit2SharpRepository(env, settingsManager, traceFactory); } else { gitRepository = new GitExeRepository(env, settingsManager, traceFactory); } IServerConfiguration serverConfiguration = new ServerConfiguration(); IAnalytics analytics = new Analytics(settingsManager, serverConfiguration, traceFactory); IWebHooksManager hooksManager = new WebHooksManager(tracer, env, hooksLock); IDeploymentStatusManager deploymentStatusManager = new DeploymentStatusManager(env, analytics, statusLock); IAutoSwapHandler autoSwapHander = new AutoSwapHandler(env, settingsManager, traceFactory); var functionManager = new FunctionManager(env, traceFactory); IDeploymentManager deploymentManager = new DeploymentManager(builderFactory, env, traceFactory, analytics, settingsManager, deploymentStatusManager, deploymentLock, GetLogger(env, level, logger), hooksManager, functionManager); var step = tracer.Step(XmlTracer.ExecutingExternalProcessTrace, new Dictionary <string, string> { { "type", "process" }, { "path", "kudu.exe" }, { "arguments", appRoot + " " + wapTargets } }); using (step) { try { deploymentManager.DeployAsync(gitRepository, changeSet: null, deployer: deployer, clean: false) .Wait(); string branch = settingsManager.GetBranch(); ChangeSet changeSet = gitRepository.GetChangeSet(branch); IDeploymentStatusFile statusFile = deploymentStatusManager.Open(changeSet.Id); if (statusFile != null && statusFile.Status == DeployStatus.Success) { autoSwapHander.HandleAutoSwap(changeSet.Id, deploymentManager.GetLogger(changeSet.Id), tracer).Wait(); } } catch (Exception e) { tracer.TraceError(e); System.Console.Error.WriteLine(e.GetBaseException().Message); System.Console.Error.WriteLine(Resources.Log_DeploymentError); return(1); } } if (logger.HasErrors) { System.Console.Error.WriteLine(Resources.Log_DeploymentError); return(1); } return(0); }
public WebHooksManagerTests() { _webHooksManager = BuildWebHooksManager(); }
public WebHooksController(ITracer tracer, WebHooksManager hooksManager) { _tracer = tracer; _hooksManager = hooksManager; }