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);
            }
        }
Example #2
0
        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);
        }