public async Task <HttpResponseMessage> InstallExtension(string id, SiteExtensionInfo requestInfo) { var startTime = DateTime.UtcNow; var tracer = _traceFactory.GetTracer(); if (IsInstallationLockHeldSafeCheck(id)) { tracer.Trace("{0} is installing with another request, reject current request with Conflict status.", id); throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.Conflict, id)); } if (requestInfo == null) { requestInfo = new SiteExtensionInfo(); } tracer.Trace("Installing {0}, version: {1} from feed: {2}", id, requestInfo.Version, requestInfo.FeedUrl); SiteExtensionInfo result = await InitInstallSiteExtension(id, requestInfo.Type); if (ArmUtils.IsArmRequest(Request)) { // create a context free tracer ITracer backgroundTracer = NullTracer.Instance; IDictionary <string, string> traceAttributes = new Dictionary <string, string>(); if (tracer.TraceLevel == TraceLevel.Off) { backgroundTracer = NullTracer.Instance; } if (tracer.TraceLevel > TraceLevel.Off) { backgroundTracer = new XmlTracer(_environment.TracePath, tracer.TraceLevel); traceAttributes = new Dictionary <string, string>() { { "url", Request.RequestUri.AbsolutePath }, { "method", Request.Method.Method } }; foreach (var item in Request.Headers) { if (!traceAttributes.ContainsKey(item.Key)) { traceAttributes.Add(item.Key, string.Join(",", item.Value)); } } } AutoResetEvent installationSignal = new AutoResetEvent(false); // trigger installation, but do not wait. Expecting poll for status ThreadPool.QueueUserWorkItem((object stateInfo) => { using (backgroundTracer.Step(XmlTracer.BackgroundTrace, attributes: traceAttributes)) { try { using (backgroundTracer.Step("Background thread started for {0} installation", id)) { _manager.InstallExtension(id, requestInfo.Version, requestInfo.FeedUrl, requestInfo.Type, backgroundTracer).Wait(); } } finally { installationSignal.Set(); // will be a few millionseconds off if task finshed within 15 seconds. LogEndEvent(id, (DateTime.UtcNow - startTime), backgroundTracer); } } }); SiteExtensionStatus armSettings = new SiteExtensionStatus(_environment.SiteExtensionSettingsPath, id, tracer); if (installationSignal.WaitOne(TimeSpan.FromSeconds(15))) { if (!armSettings.IsRestartRequired(_siteExtensionRoot)) { // only skip polling if current installation doesn`t require restart, to avoid making race condition common // TODO: re-visit if we want to skip polling for case that need to restart tracer.Trace("Installation finish quick and not require restart, skip async polling, invoking GET to return actual status to caller."); return(await GetLocalExtension(id)); } } // do not log end event here, since it is not done yet return(Request.CreateResponse(HttpStatusCode.Created, ArmUtils.AddEnvelopeOnArmRequest <SiteExtensionInfo>(result, Request))); } else { result = await _manager.InstallExtension(id, requestInfo.Version, requestInfo.FeedUrl, requestInfo.Type, tracer); if (string.Equals(Constants.SiteExtensionProvisioningStateFailed, result.ProvisioningState, StringComparison.OrdinalIgnoreCase)) { SiteExtensionStatus armSettings = new SiteExtensionStatus(_environment.SiteExtensionSettingsPath, id, tracer); throw new HttpResponseException(Request.CreateErrorResponse(armSettings.Status, result.Comment)); } var response = Request.CreateResponse(HttpStatusCode.OK, result); LogEndEvent(id, (DateTime.UtcNow - startTime), tracer); return(response); } }
public async Task <HttpResponseMessage> GetLocalExtension(string id, bool checkLatest = true) { var tracer = _traceFactory.GetTracer(); SiteExtensionInfo extension = null; HttpResponseMessage responseMessage = null; if (ArmUtils.IsArmRequest(Request)) { tracer.Trace("Incoming GetLocalExtension is arm request."); SiteExtensionStatus armSettings = new SiteExtensionStatus(_environment.SiteExtensionSettingsPath, id, tracer); if (string.Equals(Constants.SiteExtensionOperationInstall, armSettings.Operation, StringComparison.OrdinalIgnoreCase)) { var installationLock = SiteExtensionInstallationLock.CreateLock(_environment.SiteExtensionSettingsPath, id); if (!installationLock.IsHeld && string.Equals(Constants.SiteExtensionProvisioningStateSucceeded, armSettings.ProvisioningState, StringComparison.OrdinalIgnoreCase)) { tracer.Trace("Package {0} was just installed.", id); extension = await _manager.GetLocalExtension(id, checkLatest); if (extension == null) { using (tracer.Step("Status indicate {0} installed, but not able to find it from local repo.", id)) { // should NOT happen extension = new SiteExtensionInfo { Id = id }; responseMessage = Request.CreateResponse(HttpStatusCode.NotFound); // package is gone, remove setting file await armSettings.RemoveStatus(); } } else { if (SiteExtensionInstallationLock.IsAnyPendingLock(_environment.SiteExtensionSettingsPath, tracer)) { using (tracer.Step("{0} finsihed installation. But there is other installation on-going, fake the status to be Created, so that we can restart once for all.", id)) { // if there is other pending installation, fake the status extension.ProvisioningState = Constants.SiteExtensionProvisioningStateCreated; responseMessage = Request.CreateResponse(HttpStatusCode.Created, ArmUtils.AddEnvelopeOnArmRequest <SiteExtensionInfo>(extension, Request)); } } else { // it is important to call "SiteExtensionStatus.IsAnyInstallationRequireRestart" before "UpdateArmSettingsForSuccessInstallation" // since "IsAnyInstallationRequireRestart" is depending on properties inside site extension status files // while "UpdateArmSettingsForSuccessInstallation" will override some of the values bool requireRestart = SiteExtensionStatus.IsAnyInstallationRequireRestart(_environment.SiteExtensionSettingsPath, _siteExtensionRoot, tracer, _analytics); // clear operation, since opeation is done if (UpdateArmSettingsForSuccessInstallation()) { using (tracer.Step("{0} finsihed installation and batch update lock aquired. Will notify Antares GEO to restart website.", id)) { responseMessage = Request.CreateResponse(armSettings.Status, ArmUtils.AddEnvelopeOnArmRequest <SiteExtensionInfo>(extension, Request)); // Notify GEO to restart website if necessary if (requireRestart) { responseMessage.Headers.Add(Constants.SiteOperationHeaderKey, Constants.SiteOperationRestart); } } } else { tracer.Trace("Not able to aquire batch update lock, there must be another batch update on-going. return Created status to user to let them poll again."); responseMessage = Request.CreateResponse(HttpStatusCode.Created, ArmUtils.AddEnvelopeOnArmRequest <SiteExtensionInfo>(extension, Request)); } } } } else if (!installationLock.IsHeld && !armSettings.IsTerminalStatus()) { // no background thread is working on instalation // app-pool must be recycled using (tracer.Step("{0} installation cancelled, background thread must be dead.", id)) { extension = new SiteExtensionInfo { Id = id }; extension.ProvisioningState = Constants.SiteExtensionProvisioningStateCanceled; responseMessage = Request.CreateResponse(HttpStatusCode.OK, ArmUtils.AddEnvelopeOnArmRequest <SiteExtensionInfo>(extension, Request)); } } else { // on-going or failed, return status from setting using (tracer.Step("Installation {0}", armSettings.Status)) { extension = new SiteExtensionInfo { Id = id }; armSettings.FillSiteExtensionInfo(extension); responseMessage = Request.CreateResponse(armSettings.Status, ArmUtils.AddEnvelopeOnArmRequest <SiteExtensionInfo>(extension, Request)); } } } // normal GET request if (responseMessage == null) { using (tracer.Step("ARM get : {0}", id)) { extension = await _manager.GetLocalExtension(id, checkLatest); } if (extension == null) { extension = new SiteExtensionInfo { Id = id }; responseMessage = Request.CreateResponse(HttpStatusCode.NotFound); } else { armSettings.FillSiteExtensionInfo(extension); responseMessage = Request.CreateResponse(HttpStatusCode.OK, ArmUtils.AddEnvelopeOnArmRequest <SiteExtensionInfo>(extension, Request)); } } } else { using (tracer.Step("Get: {0}, is not a ARM request.", id)) { extension = await _manager.GetLocalExtension(id, checkLatest); } if (extension == null) { throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, id)); } responseMessage = Request.CreateResponse(HttpStatusCode.OK, extension); } return(responseMessage); }