// 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);
}
}