예제 #1
0
        private async Task <HttpResponseMessage> PushDeployAsync(ArtifactDeploymentInfo deploymentInfo, bool isAsync, JObject requestObject = null, ArtifactType artifactType = ArtifactType.Zip)
        {
            var content       = Request.Content;
            var isRequestJSON = content.Headers?.ContentType?.MediaType?.Equals("application/json", StringComparison.OrdinalIgnoreCase);

            if (isRequestJSON == true)
            {
                try
                {
                    // Read the request body if it hasn't been read already
                    if (requestObject == null)
                    {
                        requestObject = await Request.Content.ReadAsAsync <JObject>();
                    }
                    deploymentInfo.RemoteURL = ArmUtils.IsArmRequest(Request) ? GetArticfactURLFromARMJSON(requestObject) : GetArtifactURLFromJSON(requestObject);
                }
                catch (Exception ex)
                {
                    return(ArmUtils.CreateErrorResponse(Request, HttpStatusCode.BadRequest, ex));
                }
            }
            // For zip artifacts (zipdeploy, wardeploy, onedeploy with type=zip), copy the request body in a temp zip file.
            // It will be extracted to the appropriate directory by the Fetch handler
            else if (artifactType == ArtifactType.Zip)
            {
                if (_settings.RunFromLocalZip())
                {
                    await WriteSitePackageZip(deploymentInfo, _tracer, Request.Content);
                }
                else
                {
                    var zipFileName = Path.ChangeExtension(Path.GetRandomFileName(), "zip");
                    var zipFilePath = Path.Combine(_environment.ZipTempPath, zipFileName);

                    using (_tracer.Step("Saving request content to {0}", zipFilePath))
                    {
                        await content.CopyToAsync(zipFilePath, _tracer);
                    }

                    deploymentInfo.RepositoryUrl = zipFilePath;
                }
            }
            // Copy the request body to a temp file.
            // It will be moved to the appropriate directory by the Fetch handler
            else if (deploymentInfo.Deployer == Constants.OneDeploy)
            {
                var artifactTempPath = Path.Combine(_environment.ZipTempPath, deploymentInfo.TargetFileName);
                using (_tracer.Step("Saving request content to {0}", artifactTempPath))
                {
                    await content.CopyToAsync(artifactTempPath, _tracer);
                }

                deploymentInfo.RepositoryUrl = artifactTempPath;
            }

            isAsync = ArmUtils.IsArmRequest(Request) ? true : isAsync;

            var result = await _deploymentManager.FetchDeploy(deploymentInfo, isAsync, UriHelper.GetRequestUri(Request), "HEAD");

            var response = Request.CreateResponse();

            switch (result)
            {
            case FetchDeploymentRequestResult.RunningAynschronously:
                if (ArmUtils.IsArmRequest(Request))
                {
                    DeployResult deployResult = new DeployResult();
                    response = Request.CreateResponse(HttpStatusCode.Accepted, ArmUtils.AddEnvelopeOnArmRequest(deployResult, Request));
                    string statusURL = GetStatusUrl(Request.Headers.Referrer ?? Request.RequestUri);
                    // Should not happen: If we couldn't make the URL, there must have been an error in the request
                    if (string.IsNullOrEmpty(statusURL))
                    {
                        var badResponse = Request.CreateResponse();
                        badResponse.StatusCode = HttpStatusCode.BadRequest;
                        return(badResponse);
                    }
                    // latest deployment keyword reserved to poll till deployment done
                    response.Headers.Location = new Uri(statusURL +
                                                        String.Format("/deployments/{0}?api-version=2018-02-01&deployer={1}&time={2}", Constants.LatestDeployment, deploymentInfo.Deployer, DateTime.UtcNow.ToString("yyy-MM-dd_HH-mm-ssZ")));
                }
                else if (isAsync)
                {
                    // latest deployment keyword reserved to poll till deployment done
                    response.Headers.Location = new Uri(UriHelper.GetRequestUri(Request),
                                                        String.Format("/api/deployments/{0}?deployer={1}&time={2}", Constants.LatestDeployment, deploymentInfo.Deployer, DateTime.UtcNow.ToString("yyy-MM-dd_HH-mm-ssZ")));
                }
                response.StatusCode = HttpStatusCode.Accepted;
                break;

            case FetchDeploymentRequestResult.ForbiddenScmDisabled:
                // Should never hit this for zip push deploy
                response.StatusCode = HttpStatusCode.Forbidden;
                _tracer.Trace("Scm is not enabled, reject all requests.");
                break;

            case FetchDeploymentRequestResult.ConflictAutoSwapOngoing:
                response.StatusCode = HttpStatusCode.Conflict;
                response.Content    = new StringContent(Resources.Error_AutoSwapDeploymentOngoing);
                break;

            case FetchDeploymentRequestResult.Pending:
                // Shouldn't happen here, as we disallow deferral for this use case
                response.StatusCode = HttpStatusCode.Accepted;
                break;

            case FetchDeploymentRequestResult.RanSynchronously:
                response.StatusCode = HttpStatusCode.OK;
                break;

            case FetchDeploymentRequestResult.ConflictDeploymentInProgress:
                response.StatusCode = HttpStatusCode.Conflict;
                response.Content    = new StringContent(Resources.Error_DeploymentInProgress);
                break;

            case FetchDeploymentRequestResult.ConflictRunFromRemoteZipConfigured:
                response.StatusCode = HttpStatusCode.Conflict;
                response.Content    = new StringContent(Resources.Error_RunFromRemoteZipConfigured);
                break;

            default:
                response.StatusCode = HttpStatusCode.BadRequest;
                break;
            }

            return(response);
        }
예제 #2
0
        public async Task <HttpResponseMessage> Get(string name)
        {
            var tracer = _traceFactory.GetTracer();

            using (tracer.Step($"FunctionsController.Get({name})"))
            {
                return(Request.CreateResponse(HttpStatusCode.OK,
                                              ArmUtils.AddEnvelopeOnArmRequest(
                                                  AddFunctionAppIdToEnvelope(await _manager.GetFunctionConfigAsync(name, ArmUtils.IsArmRequest(Request) ? new FunctionTestData() : null)), Request)));
            }
        }
예제 #3
0
        public async Task <HttpResponseMessage> OneDeploy(
            [FromUri] string type  = null,
            [FromUri] bool async   = false,
            [FromUri] string path  = null,
            [FromUri] bool restart = true,
            [FromUri] string stack = null
            )
        {
            using (_tracer.Step("OnePushDeploy"))
            {
                JObject requestObject = null;

                try
                {
                    if (ArmUtils.IsArmRequest(Request))
                    {
                        requestObject = await Request.Content.ReadAsAsync <JObject>();

                        var armProperties = requestObject.Value <JObject>("properties");
                        type    = armProperties.Value <string>("type");
                        async   = armProperties.Value <bool>("async");
                        path    = armProperties.Value <string>("path");
                        restart = armProperties.Value <bool>("restart");
                        stack   = armProperties.Value <string>("stack");
                    }
                }
                catch (Exception ex)
                {
                    return(ArmUtils.CreateErrorResponse(Request, HttpStatusCode.BadRequest, ex));
                }

                //
                // 'async' is not a CSharp-ish variable name. And although it is a valid variable name, some
                // IDEs confuse it to be the 'async' keyword in C#.
                // On the other hand, isAsync is not a good name for the query-parameter.
                // So we use 'async' as the query parameter, and then assign it to the C# variable 'isAsync'
                // at the earliest. Hereon, we use just 'isAsync'.
                //
                bool isAsync = async;

                var deploymentInfo = new ArtifactDeploymentInfo(_environment, _traceFactory)
                {
                    AllowDeploymentWhileScmDisabled = true,
                    Deployer                = Constants.OneDeploy,
                    IsContinuous            = false,
                    AllowDeferredDeployment = false,
                    IsReusable              = false,
                    TargetChangeset         = DeploymentManager.CreateTemporaryChangeSet(message: "OneDeploy"),
                    CommitId                = null,
                    RepositoryType          = RepositoryType.None,
                    Fetch = OneDeployFetch,
                    DoFullBuildByDefault = false,
                    Message            = "OneDeploy",
                    WatchedFileEnabled = false,
                    RestartAllowed     = restart,
                };

                string websiteStack = !string.IsNullOrWhiteSpace(stack) ? stack : _settings.GetValue(Constants.StackEnvVarName);

                ArtifactType artifactType = ArtifactType.Invalid;
                try
                {
                    artifactType = (ArtifactType)Enum.Parse(typeof(ArtifactType), type, ignoreCase: true);
                }
                catch
                {
                    return(Request.CreateResponse(HttpStatusCode.BadRequest, $"type='{type}' not recognized"));
                }

                switch (artifactType)
                {
                case ArtifactType.War:
                    if (!string.Equals(websiteStack, Constants.Tomcat, StringComparison.OrdinalIgnoreCase))
                    {
                        return(Request.CreateResponse(HttpStatusCode.BadRequest, $"WAR files cannot be deployed to stack='{websiteStack}'. Expected stack='TOMCAT'"));
                    }

                    // Support for legacy war deployments
                    // Sets TargetDirectoryPath then deploys the War file as a Zip so it can be extracted
                    // then deployed
                    if (!string.IsNullOrWhiteSpace(path))
                    {
                        //
                        // For legacy war deployments, the only path allowed is site/wwwroot/webapps/<directory-name>
                        //

                        var segments = path.Split('/');
                        if (segments.Length != 4 || !path.StartsWith("site/wwwroot/webapps/") || string.IsNullOrWhiteSpace(segments[3]))
                        {
                            return(Request.CreateResponse(HttpStatusCode.BadRequest, $"path='{path}'. Only allowed path when type={artifactType} is site/wwwroot/webapps/<directory-name>. Example: path=site/wwwroot/webapps/ROOT"));
                        }

                        deploymentInfo.TargetDirectoryPath = Path.Combine(_environment.RootPath, path);
                        deploymentInfo.Fetch = LocalZipHandler;
                        deploymentInfo.CleanupTargetDirectory = true;
                        artifactType = ArtifactType.Zip;
                    }
                    else
                    {
                        // For type=war, the target file is app.war
                        // As we want app.war to be deployed to wwwroot, no need to configure TargetDirectoryPath
                        deploymentInfo.TargetFileName = "app.war";
                    }

                    break;

                case ArtifactType.Jar:
                    if (!string.Equals(websiteStack, Constants.JavaSE, StringComparison.OrdinalIgnoreCase))
                    {
                        return(Request.CreateResponse(HttpStatusCode.BadRequest, $"JAR files cannot be deployed to stack='{websiteStack}'. Expected stack='JAVASE'"));
                    }

                    deploymentInfo.TargetFileName = "app.jar";

                    break;

                case ArtifactType.Ear:
                    // Currently not supported on Windows but here for future use
                    if (!string.Equals(websiteStack, Constants.JBossEap, StringComparison.OrdinalIgnoreCase))
                    {
                        return(Request.CreateResponse(HttpStatusCode.BadRequest, $"EAR files cannot be deployed to stack='{websiteStack}'. Expected stack='JBOSSEAP'"));
                    }

                    deploymentInfo.TargetFileName = "app.ear";

                    break;

                case ArtifactType.Lib:
                    if (string.IsNullOrWhiteSpace(path))
                    {
                        return(Request.CreateResponse(HttpStatusCode.BadRequest, $"Path must be defined for library deployments"));
                    }

                    SetTargetFromPath(deploymentInfo, path);

                    break;

                case ArtifactType.Startup:
                    SetTargetFromPath(deploymentInfo, GetStartupFileName());

                    break;

                case ArtifactType.Static:
                    if (string.IsNullOrWhiteSpace(path))
                    {
                        return(Request.CreateResponse(HttpStatusCode.BadRequest, $"Path must be defined for static file deployments"));
                    }

                    SetTargetFromPath(deploymentInfo, path);

                    break;

                case ArtifactType.Zip:
                    deploymentInfo.Fetch = LocalZipHandler;

                    break;

                default:
                    return(Request.CreateResponse(HttpStatusCode.BadRequest, $"Artifact type '{artifactType}' not supported"));
                }

                return(await PushDeployAsync(deploymentInfo, isAsync, requestObject, artifactType));
            }
        }
예제 #4
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))
                {
                    bool isInstallationLockHeld = IsInstallationLockHeldSafeCheck(id);
                    if (!isInstallationLockHeld &&
                        string.Equals(Constants.SiteExtensionProvisioningStateSucceeded, armSettings.ProvisioningState, StringComparison.OrdinalIgnoreCase))
                    {
                        tracer.Trace("Package {0} was just installed.", id);
                        extension = await ThrowsConflictIfIOException(_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} finished 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} finished 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 (!isInstallationLockHeld && !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 ThrowsConflictIfIOException(_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 ThrowsConflictIfIOException(_manager.GetLocalExtension(id, checkLatest));
                }

                if (extension == null)
                {
                    throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, id));
                }

                responseMessage = Request.CreateResponse(HttpStatusCode.OK, extension);
            }

            return(responseMessage);
        }
        public async Task <HttpResponseMessage> Deploy(string id = null)
        {
            JObject result = GetJsonContent();

            // Just block here to read the json payload from the body
            using (_tracer.Step("DeploymentService.Deploy(id)"))
            {
                HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);
                await _deploymentLock.LockHttpOperationAsync(async() =>
                {
                    try
                    {
                        if (PostDeploymentHelper.IsAutoSwapOngoing())
                        {
                            throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.Conflict, Resources.Error_AutoSwapDeploymentOngoing));
                        }

                        DeployResult deployResult;
                        if (TryParseDeployResult(id, result, out deployResult))
                        {
                            using (_tracer.Step("DeploymentService.Create(id)"))
                            {
                                CreateDeployment(deployResult, result.Value <string>("details"));

                                // e.g if final url is "https://kudutry.scm.azurewebsites.net/api/deployments/ef52ec67fc9574e726955a9cbaf7bcba791e4e95/log"
                                // deploymentUri should be "https://kudutry.scm.azurewebsites.net/api/deployments/ef52ec67fc9574e726955a9cbaf7bcba791e4e95"
                                Uri deploymentUri   = UriHelper.MakeRelative(UriHelper.GetBaseUri(Request), Request.RequestUri.AbsolutePath);
                                deployResult.Url    = deploymentUri;
                                deployResult.LogUrl = UriHelper.MakeRelative(deploymentUri, "log");

                                response = Request.CreateResponse(HttpStatusCode.OK, ArmUtils.AddEnvelopeOnArmRequest(deployResult, Request));
                                return;
                            }
                        }

                        bool clean          = false;
                        bool needFileUpdate = true;

                        if (result != null)
                        {
                            clean = result.Value <bool>("clean");
                            JToken needFileUpdateToken;
                            if (result.TryGetValue("needFileUpdate", out needFileUpdateToken))
                            {
                                needFileUpdate = needFileUpdateToken.Value <bool>();
                            }
                        }

                        string username = null;
                        AuthUtility.TryExtractBasicAuthUser(Request, out username);

                        IRepository repository = _repositoryFactory.GetRepository();
                        if (repository == null)
                        {
                            throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, Resources.Error_RepositoryNotFound));
                        }
                        ChangeSet changeSet = null;
                        if (!String.IsNullOrEmpty(id))
                        {
                            changeSet = repository.GetChangeSet(id);
                            if (changeSet == null)
                            {
                                string message = String.Format(CultureInfo.CurrentCulture, Resources.Error_DeploymentNotFound, id);
                                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, message));
                            }
                        }

                        try
                        {
                            await _deploymentManager.DeployAsync(repository, changeSet, username, clean, deploymentInfo: null, needFileUpdate: needFileUpdate);
                        }
                        catch (DeploymentFailedException ex)
                        {
                            if (!ArmUtils.IsArmRequest(Request))
                            {
                                throw;
                            }

                            // if requests comes thru ARM, we adjust the error code from 500 -> 400
                            throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex.ToString()));
                        }

                        // auto-swap
                        if (PostDeploymentHelper.IsAutoSwapEnabled())
                        {
                            if (changeSet == null)
                            {
                                var targetBranch = _settings.GetBranch();
                                changeSet        = repository.GetChangeSet(targetBranch);
                            }

                            IDeploymentStatusFile statusFile = _status.Open(changeSet.Id);
                            if (statusFile != null && statusFile.Status == DeployStatus.Success)
                            {
                                await PostDeploymentHelper.PerformAutoSwap(_environment.RequestId, new PostDeploymentTraceListener(_tracer, _deploymentManager.GetLogger(changeSet.Id)));
                            }
                        }
                    }
                    catch (FileNotFoundException ex)
                    {
                        throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, ex));
                    }
                }, "Performing deployment");

                return(response);
            }
        }
예제 #6
0
        public async Task <HttpResponseMessage> OneDeploy(
            [FromUri] string type      = null,
            [FromUri] bool async       = false,
            [FromUri] string path      = null,
            [FromUri] bool?restart     = true,
            [FromUri] bool?clean       = null,
            [FromUri] bool ignoreStack = false
            )
        {
            using (_tracer.Step(Constants.OneDeploy))
            {
                JObject requestObject = null;

                try
                {
                    if (ArmUtils.IsArmRequest(Request))
                    {
                        requestObject = await Request.Content.ReadAsAsync <JObject>();

                        var armProperties = requestObject.Value <JObject>("properties");

                        type        = armProperties.Value <string>("type");
                        async       = armProperties.Value <bool>("async");
                        path        = armProperties.Value <string>("path");
                        restart     = armProperties.Value <bool?>("restart");
                        clean       = armProperties.Value <bool?>("clean");
                        ignoreStack = armProperties.Value <bool>("ignorestack");
                    }
                }
                catch (Exception ex)
                {
                    return(ArmUtils.CreateErrorResponse(Request, HttpStatusCode.BadRequest, ex));
                }

                //
                // 'async' is not a CSharp-ish variable name. And although it is a valid variable name, some
                // IDEs confuse it to be the 'async' keyword in C#.
                // On the other hand, isAsync is not a good name for the query-parameter.
                // So we use 'async' as the query parameter, and then assign it to the C# variable 'isAsync'
                // at the earliest. Hereon, we use just 'isAsync'.
                //
                bool isAsync = async;

                ArtifactType artifactType = ArtifactType.Unknown;
                try
                {
                    artifactType = (ArtifactType)Enum.Parse(typeof(ArtifactType), type, ignoreCase: true);
                }
                catch
                {
                    return(StatusCode400($"type='{type}' not recognized"));
                }

                var deploymentInfo = new ArtifactDeploymentInfo(_environment, _traceFactory)
                {
                    AllowDeploymentWhileScmDisabled = true,
                    Deployer                = Constants.OneDeploy,
                    IsContinuous            = false,
                    AllowDeferredDeployment = false,
                    IsReusable              = false,
                    TargetRootPath          = _environment.WebRootPath,
                    TargetChangeset         = DeploymentManager.CreateTemporaryChangeSet(message: Constants.OneDeploy),
                    CommitId                = null,
                    RepositoryType          = RepositoryType.None,
                    Fetch = OneDeployFetch,
                    DoFullBuildByDefault = false,
                    Message                = Constants.OneDeploy,
                    WatchedFileEnabled     = false,
                    CleanupTargetDirectory = clean.GetValueOrDefault(false),
                    RestartAllowed         = restart.GetValueOrDefault(true),
                };

                string error;
                switch (artifactType)
                {
                case ArtifactType.War:
                    if (!OneDeployHelper.EnsureValidStack(OneDeployHelper.Tomcat, ignoreStack, out error))
                    {
                        return(StatusCode400(error));
                    }

                    // If path is non-null, we assume this is a legacy war deployment, i.e. equivalent of wardeploy
                    if (!string.IsNullOrWhiteSpace(path))
                    {
                        //
                        // For legacy war deployments, the only path allowed is webapps/<directory-name>
                        //

                        if (!OneDeployHelper.IsLegacyWarPathValid(path))
                        {
                            return(StatusCode400($"path='{path}'. Only allowed path when type={artifactType} is webapps/<directory-name>. Example: path=webapps/ROOT"));
                        }

                        deploymentInfo.TargetRootPath = Path.Combine(_environment.WebRootPath, path);
                        deploymentInfo.Fetch          = LocalZipHandler;

                        // Legacy war deployment is equivalent to wardeploy
                        // So always do clean deploy.
                        deploymentInfo.CleanupTargetDirectory = true;
                        artifactType = ArtifactType.Zip;
                    }
                    else
                    {
                        // For type=war, if no path is specified, the target file is app.war
                        deploymentInfo.TargetFileName = "app.war";
                    }

                    break;

                case ArtifactType.Jar:
                    if (!OneDeployHelper.EnsureValidStack(OneDeployHelper.JavaSE, ignoreStack, out error))
                    {
                        return(StatusCode400(error));
                    }

                    deploymentInfo.TargetFileName = "app.jar";
                    break;

                case ArtifactType.Ear:
                    if (!OneDeployHelper.EnsureValidStack(OneDeployHelper.JBossEap, ignoreStack, out error))
                    {
                        return(StatusCode400(error));
                    }

                    deploymentInfo.TargetFileName = "app.ear";
                    break;

                case ArtifactType.Lib:
                    if (!OneDeployHelper.EnsureValidPath(artifactType, path, out error))
                    {
                        return(StatusCode400(error));
                    }

                    deploymentInfo.TargetRootPath = OneDeployHelper.GetLibsDirectoryAbsolutePath(_environment);
                    OneDeployHelper.SetTargetSubDirectoyAndFileNameFromPath(deploymentInfo, path);
                    break;

                case ArtifactType.Startup:
                    deploymentInfo.TargetRootPath = OneDeployHelper.GetScriptsDirectoryAbsolutePath(_environment);
                    OneDeployHelper.SetTargetSubDirectoyAndFileNameFromPath(deploymentInfo, OneDeployHelper.GetStartupFileName());
                    break;

                case ArtifactType.Script:
                    if (!OneDeployHelper.EnsureValidPath(artifactType, path, out error))
                    {
                        return(StatusCode400(error));
                    }

                    deploymentInfo.TargetRootPath = OneDeployHelper.GetScriptsDirectoryAbsolutePath(_environment);
                    OneDeployHelper.SetTargetSubDirectoyAndFileNameFromPath(deploymentInfo, path);

                    break;

                case ArtifactType.Static:
                    if (!OneDeployHelper.EnsureValidPath(artifactType, path, out error))
                    {
                        return(StatusCode400(error));
                    }

                    OneDeployHelper.SetTargetSubDirectoyAndFileNameFromPath(deploymentInfo, path);

                    break;

                case ArtifactType.Zip:
                    deploymentInfo.Fetch = LocalZipHandler;
                    deploymentInfo.TargetSubDirectoryRelativePath = path;

                    // Deployments for type=zip default to clean=true
                    deploymentInfo.CleanupTargetDirectory = clean.GetValueOrDefault(true);

                    break;

                default:
                    return(StatusCode400($"Artifact type '{artifactType}' not supported"));
                }

                return(await PushDeployAsync(deploymentInfo, isAsync, requestObject, artifactType));
            }
        }
예제 #7
0
        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 = new CascadeTracer(new XmlTracer(_environment.TracePath, tracer.TraceLevel), new ETWTracer(_environment.RequestId, "PUT"));
                    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, requestInfo.InstallationArgs).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, requestInfo.InstallationArgs);

                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);
            }
        }
예제 #8
0
        private ProcessInfo GetProcessInfo(Process process, string href, bool details = false)
        {
            href = href.TrimEnd('/');
            if (href.EndsWith("/0", StringComparison.OrdinalIgnoreCase))
            {
                href = href.Substring(0, href.Length - 1) + process.Id;
            }

            var selfLink = new Uri(href);
            var info     = new ProcessInfo
            {
                Id       = process.Id,
                Name     = process.ProcessName,
                Href     = selfLink,
                UserName = SafeGetValue(process.GetUserName, null)
            };

            if (details)
            {
                // this could fail access denied
                info.HandleCount = SafeGetValue(() => process.HandleCount, -1);
                info.ThreadCount = SafeGetValue(() => process.Threads.Count, -1);
                info.ModuleCount = SafeGetValue(() => process.Modules.Count, -1);
                info.FileName    = SafeGetValue(() => process.MainModule.FileName, "N/A");

                // always return empty
                //info.Arguments = SafeGetValue(() => process.StartInfo.Arguments, "N/A");

                info.StartTime               = SafeGetValue(() => process.StartTime.ToUniversalTime(), DateTime.MinValue);
                info.TotalProcessorTime      = SafeGetValue(() => process.TotalProcessorTime, TimeSpan.FromSeconds(-1));
                info.UserProcessorTime       = SafeGetValue(() => process.UserProcessorTime, TimeSpan.FromSeconds(-1));
                info.PrivilegedProcessorTime = SafeGetValue(() => process.PrivilegedProcessorTime, TimeSpan.FromSeconds(-1));

                info.PagedSystemMemorySize64    = SafeGetValue(() => process.PagedSystemMemorySize64, -1);
                info.NonpagedSystemMemorySize64 = SafeGetValue(() => process.NonpagedSystemMemorySize64, -1);
                info.PagedMemorySize64          = SafeGetValue(() => process.PagedMemorySize64, -1);
                info.PeakPagedMemorySize64      = SafeGetValue(() => process.PeakPagedMemorySize64, -1);
                info.WorkingSet64            = SafeGetValue(() => process.WorkingSet64, -1);
                info.PeakWorkingSet64        = SafeGetValue(() => process.PeakWorkingSet64, -1);
                info.VirtualMemorySize64     = SafeGetValue(() => process.VirtualMemorySize64, -1);
                info.PeakVirtualMemorySize64 = SafeGetValue(() => process.PeakVirtualMemorySize64, -1);
                info.PrivateMemorySize64     = SafeGetValue(() => process.PrivateMemorySize64, -1);

                info.MiniDump                   = new Uri(selfLink + "/dump");
                info.OpenFileHandles            = SafeGetValue(() => GetOpenFileHandles(process.Id), Enumerable.Empty <string>());
                info.Parent                     = new Uri(selfLink, SafeGetValue(() => process.GetParentId(_tracer), 0).ToString());
                info.Children                   = SafeGetValue(() => process.GetChildren(_tracer, recursive: false), Enumerable.Empty <Process>()).Select(c => new Uri(selfLink, c.Id.ToString()));
                info.Threads                    = SafeGetValue(() => GetThreads(process, selfLink.ToString()), Enumerable.Empty <ProcessThreadInfo>());
                info.Modules                    = SafeGetValue(() => GetModules(process, selfLink.ToString().TrimEnd('/') + "/modules"), Enumerable.Empty <ProcessModuleInfo>());
                info.TimeStamp                  = DateTime.UtcNow;
                info.EnvironmentVariables       = SafeGetValue(process.GetEnvironmentVariables, new Dictionary <string, string>());
                info.CommandLine                = SafeGetValue(process.GetCommandLine, null);
                info.IsProfileRunning           = ProfileManager.IsProfileRunning(process.Id);
                info.IsIisProfileRunning        = ProfileManager.IsIisProfileRunning(process.Id);
                info.IisProfileTimeoutInSeconds = ProfileManager.IisProfileTimeoutInSeconds;
                SetEnvironmentInfo(info);

                if (ArmUtils.IsArmRequest(Request))
                {
                    // No need to hide the secrets if the request is from contributor or admin.
                    if (!(ArmUtils.IsRbacContributorRequest(Request) || ArmUtils.IsLegacyAuthorizationSource(Request)))
                    {
                        info.EnvironmentVariables = info.EnvironmentVariables
                                                    .Where(kv => _armWhitelistedVariables.Contains(kv.Key, StringComparer.OrdinalIgnoreCase))
                                                    .ToDictionary(k => k.Key, v => v.Value);
                        info.CommandLine = null;
                    }
                }
            }

            return(info);
        }
        public async Task <IActionResult> OneDeploy(
            [FromQuery] string type      = null,
            [FromQuery] bool async       = false,
            [FromQuery] string path      = null,
            [FromQuery] bool?restart     = true,
            [FromQuery] bool?clean       = null,
            [FromQuery] bool ignoreStack = false
            )
        {
            string remoteArtifactUrl = null;

            using (_tracer.Step(Constants.OneDeploy))
            {
                string deploymentId = GetExternalDeploymentId(Request);

                try
                {
                    if (Request.MediaTypeContains("application/json"))
                    {
                        string jsonString;
                        using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
                        {
                            jsonString = await reader.ReadToEndAsync();
                        }

                        var requestJson = JObject.Parse(jsonString);

                        if (ArmUtils.IsArmRequest(Request))
                        {
                            requestJson = requestJson.Value <JObject>("properties");

                            type        = requestJson.Value <string>("type");
                            async       = requestJson.Value <bool>("async");
                            path        = requestJson.Value <string>("path");
                            restart     = requestJson.Value <bool?>("restart");
                            clean       = requestJson.Value <bool?>("clean");
                            ignoreStack = requestJson.Value <bool>("ignorestack");
                        }

                        remoteArtifactUrl = GetArtifactURLFromJSON(requestJson);
                    }
                }
                catch (Exception ex)
                {
                    return(StatusCode400(ex.ToString()));
                }

                //
                // 'async' is not a CSharp-ish variable name. And although it is a valid variable name, some
                // IDEs confuse it to be the 'async' keyword in C#.
                // On the other hand, isAsync is not a good name for the query-parameter.
                // So we use 'async' as the query parameter, and then assign it to the C# variable 'isAsync'
                // at the earliest. Hereon, we use just 'isAsync'.
                //
                bool isAsync = async;

                ArtifactType artifactType = ArtifactType.Unknown;
                try
                {
                    artifactType = (ArtifactType)Enum.Parse(typeof(ArtifactType), type, ignoreCase: true);
                }
                catch
                {
                    return(StatusCode400($"type='{type}' not recognized"));
                }

                var deploymentInfo = new ArtifactDeploymentInfo(_environment, _traceFactory)
                {
                    ArtifactType = artifactType,
                    AllowDeploymentWhileScmDisabled = true,
                    Deployer                = Constants.OneDeploy,
                    IsContinuous            = false,
                    AllowDeferredDeployment = false,
                    IsReusable              = false,
                    TargetRootPath          = _environment.WebRootPath,
                    TargetChangeset         = DeploymentManager.CreateTemporaryChangeSet(message: Constants.OneDeploy),
                    CommitId                = null,
                    ExternalDeploymentId    = deploymentId,
                    RepositoryType          = RepositoryType.None,
                    RemoteURL               = remoteArtifactUrl,
                    Fetch = OneDeployFetch,
                    DoFullBuildByDefault = false,
                    Message                = Constants.OneDeploy,
                    WatchedFileEnabled     = false,
                    CleanupTargetDirectory = clean.GetValueOrDefault(false),
                    RestartAllowed         = restart.GetValueOrDefault(true),
                };

                string error;
                switch (artifactType)
                {
                case ArtifactType.War:
                    if (!OneDeployHelper.EnsureValidStack(artifactType, new List <string> {
                        OneDeployHelper.Tomcat, OneDeployHelper.JBossEap
                    }, ignoreStack, out error))
                    {
                        return(StatusCode400(error));
                    }

                    // If path is non-null, we assume this is a legacy war deployment, i.e. equivalent of wardeploy
                    if (!string.IsNullOrWhiteSpace(path))
                    {
                        //
                        // For legacy war deployments, the only path allowed is webapps/<directory-name>
                        //

                        if (!OneDeployHelper.EnsureValidPath(artifactType, OneDeployHelper.WwwrootDirectoryRelativePath, ref path, out error))
                        {
                            return(StatusCode400(error));
                        }

                        if (!OneDeployHelper.IsLegacyWarPathValid(path))
                        {
                            return(StatusCode400($"path='{path}' is invalid. When type={artifactType}, the only allowed paths are webapps/<directory-name> or /home/site/wwwroot/webapps/<directory-name>. " +
                                                 $"Example: path=webapps/ROOT or path=/home/site/wwwroot/webapps/ROOT"));
                        }

                        deploymentInfo.TargetRootPath = Path.Combine(_environment.WebRootPath, path);
                        deploymentInfo.Fetch          = LocalZipHandler;

                        // Legacy war deployment is equivalent to wardeploy
                        // So always do clean deploy.
                        deploymentInfo.CleanupTargetDirectory = true;
                        artifactType = ArtifactType.Zip;
                    }
                    else
                    {
                        // For type=war, if no path is specified, the target file is app.war
                        deploymentInfo.TargetFileName = "app.war";
                    }

                    break;

                case ArtifactType.Jar:
                    if (!OneDeployHelper.EnsureValidStack(artifactType, new List <string> {
                        OneDeployHelper.JavaSE
                    }, ignoreStack, out error))
                    {
                        return(StatusCode400(error));
                    }

                    deploymentInfo.TargetFileName = "app.jar";
                    break;

                case ArtifactType.Ear:
                    if (!OneDeployHelper.EnsureValidStack(artifactType, new List <string> {
                        OneDeployHelper.JBossEap
                    }, ignoreStack, out error))
                    {
                        return(StatusCode400(error));
                    }

                    deploymentInfo.TargetFileName = "app.ear";
                    break;

                case ArtifactType.Lib:
                    if (!OneDeployHelper.EnsureValidPath(artifactType, OneDeployHelper.LibsDirectoryRelativePath, ref path, out error))
                    {
                        return(StatusCode400(error));
                    }

                    deploymentInfo.TargetRootPath = OneDeployHelper.GetAbsolutePath(_environment, OneDeployHelper.LibsDirectoryRelativePath);
                    OneDeployHelper.SetTargetSubDirectoyAndFileNameFromRelativePath(deploymentInfo, path);
                    break;

                case ArtifactType.Startup:
                    deploymentInfo.TargetRootPath = OneDeployHelper.GetAbsolutePath(_environment, OneDeployHelper.ScriptsDirectoryRelativePath);
                    OneDeployHelper.SetTargetSubDirectoyAndFileNameFromRelativePath(deploymentInfo, OneDeployHelper.GetStartupFileName());
                    break;

                case ArtifactType.Script:
                    if (!OneDeployHelper.EnsureValidPath(artifactType, OneDeployHelper.ScriptsDirectoryRelativePath, ref path, out error))
                    {
                        return(StatusCode400(error));
                    }

                    deploymentInfo.TargetRootPath = OneDeployHelper.GetAbsolutePath(_environment, OneDeployHelper.ScriptsDirectoryRelativePath);
                    OneDeployHelper.SetTargetSubDirectoyAndFileNameFromRelativePath(deploymentInfo, path);

                    break;

                case ArtifactType.Static:
                    if (!OneDeployHelper.EnsureValidPath(artifactType, OneDeployHelper.WwwrootDirectoryRelativePath, ref path, out error))
                    {
                        return(StatusCode400(error));
                    }

                    OneDeployHelper.SetTargetSubDirectoyAndFileNameFromRelativePath(deploymentInfo, path);

                    break;

                case ArtifactType.Zip:
                    deploymentInfo.Fetch = LocalZipHandler;
                    deploymentInfo.TargetSubDirectoryRelativePath = path;

                    // Deployments for type=zip default to clean=true
                    deploymentInfo.CleanupTargetDirectory = clean.GetValueOrDefault(true);

                    break;

                default:
                    return(StatusCode400($"Artifact type '{artifactType}' not supported"));
                }

                return(await PushDeployAsync(deploymentInfo, isAsync, HttpContext));
            }
        }
예제 #10
0
 private Uri GetResponseLocation(HttpRequestMessage request)
 {
     return((ArmUtils.IsArmRequest(request) && request.Headers.Referrer != null) ? request.Headers.Referrer : Request.RequestUri);
 }
예제 #11
0
        public HttpResponseMessage GetResult(string id)
        {
            using (_tracer.Step("DeploymentService.GetResult"))
            {
                var latestEtag = (ScmHostingConfigurations.GetLatestDeploymentOptimized && string.Equals(Constants.LatestDeployment, id)) ? GetCurrentEtag(Request) : null;
                if (TryGetCachedLatestDeployment(latestEtag, out HttpResponseMessage cachedResponse))
                {
                    return(cachedResponse);
                }

                if (IsLatestPendingDeployment(Request, id, out DeployResult pending, out DeployResult latest))
                {
                    _cachedLatestDeployment = (latestEtag != null && pending != null) ? new DeploymentCacheItem {
                        Etag = latestEtag, Result = pending
                    } : null;

                    var response = Request.CreateResponse(HttpStatusCode.Accepted, ArmUtils.AddEnvelopeOnArmRequest(pending, Request));
                    response.Headers.Location   = GetResponseLocation(Request);
                    response.Headers.RetryAfter = new RetryConditionHeaderValue(TimeSpan.FromSeconds(ScmHostingConfigurations.ArmRetryAfterSeconds));
                    return(response);
                }

                _cachedLatestDeployment = (latestEtag != null && latest != null) ? new DeploymentCacheItem {
                    Etag = latestEtag, Result = latest
                } : null;

                var result = latest ?? _deploymentManager.GetResult(id);
                if (result == null)
                {
                    var response = Request.CreateErrorResponse(HttpStatusCode.NotFound, String.Format(CultureInfo.CurrentCulture,
                                                                                                      Resources.Error_DeploymentNotFound,
                                                                                                      id));
                    throw new HttpResponseException(response);
                }

                Uri baseUri = UriHelper.MakeRelative(Request.GetBaseUri(), Request.RequestUri.AbsolutePath);
                result.Url    = baseUri;
                result.LogUrl = UriHelper.MakeRelative(baseUri, "log");

                if (ScmHostingConfigurations.GetLatestDeploymentOptimized &&
                    (result.Status == DeployStatus.Building || result.Status == DeployStatus.Deploying || result.Status == DeployStatus.Pending) &&
                    (ArmUtils.IsAzureResourceManagerUserAgent(Request) || ArmUtils.IsVSTSDevOpsUserAgent(Request)))
                {
                    var responseMessage = Request.CreateResponse(HttpStatusCode.Accepted, ArmUtils.AddEnvelopeOnArmRequest(result, Request));
                    responseMessage.Headers.Location   = GetResponseLocation(Request);
                    responseMessage.Headers.RetryAfter = new RetryConditionHeaderValue(TimeSpan.FromSeconds(ScmHostingConfigurations.ArmRetryAfterSeconds));
                    return(responseMessage);
                }

                if (ArmUtils.IsArmRequest(Request))
                {
                    switch (result.Status)
                    {
                    case DeployStatus.Building:
                    case DeployStatus.Deploying:
                    case DeployStatus.Pending:
                        var responseMessage = Request.CreateResponse(HttpStatusCode.OK, ArmUtils.AddEnvelopeOnArmRequest(result, Request));
                        responseMessage.Headers.Location   = GetResponseLocation(Request);
                        responseMessage.Headers.RetryAfter = new RetryConditionHeaderValue(TimeSpan.FromSeconds(ScmHostingConfigurations.ArmRetryAfterSeconds));
                        return(responseMessage);

                    case DeployStatus.Failed:
                    case DeployStatus.Success:
                        break;

                    default:
                        return(Request.CreateResponse(HttpStatusCode.BadRequest, ArmUtils.AddEnvelopeOnArmRequest(result, Request)));
                    }
                }
                return(Request.CreateResponse(HttpStatusCode.OK, ArmUtils.AddEnvelopeOnArmRequest(result, Request)));
            }
        }
예제 #12
0
        protected override async Task <HttpResponseMessage> CreateDirectoryPutResponse(DirectoryInfoBase info, string localFilePath)
        {
            try
            {
                var     isRequestJSON  = Request.Content.Headers?.ContentType?.MediaType?.Equals("application/json", StringComparison.OrdinalIgnoreCase);
                var     targetPath     = localFilePath;
                var     isArmTemplate  = false;
                JObject requestContent = null;
                Uri     packageUri     = null;
                if (isRequestJSON == true)
                {
                    requestContent = await Request.Content.ReadAsAsync <JObject>();

                    var payload = requestContent;
                    if (ArmUtils.IsArmRequest(Request))
                    {
                        payload       = payload.Value <JObject>("properties");
                        isArmTemplate = ArmUtils.IsAzureResourceManagerUserAgent(Request);
                    }

                    var uri = payload?.Value <string>("packageUri");
                    if (!Uri.TryCreate(uri, UriKind.Absolute, out packageUri))
                    {
                        throw new InvalidOperationException($"Payload contains invalid '{uri}' packageUri property");
                    }

                    var path = payload?.Value <string>("path");
                    if (!string.IsNullOrEmpty(path))
                    {
                        targetPath = Path.Combine(targetPath, path);
                        FileSystemHelpers.CreateDirectory(targetPath);
                    }
                }

                using (packageUri == null ? Tracer.Step($"Extracting content to {targetPath}")
                    : Tracer.Step("Extracting content from {0} to {1}", StringUtils.ObfuscatePath(packageUri.AbsoluteUri), targetPath))
                {
                    var content = packageUri == null ? Request.Content
                        : await DeploymentHelper.GetArtifactContentFromURL(new ArtifactDeploymentInfo(null, null) { RemoteURL = packageUri.AbsoluteUri }, Tracer);

                    using (var stream = await content.ReadAsStreamAsync())
                    {
                        // The unzipping is done over the existing folder, without first removing existing files.
                        // Hence it's more of a PATCH than a PUT. We should consider supporting both with the right semantic.
                        // Though a true PUT at the root would be scary as it would wipe all existing files!
                        var zipArchive = new ZipArchive(stream, ZipArchiveMode.Read);
                        zipArchive.Extract(targetPath, Tracer);
                    }
                }

                if (isArmTemplate && requestContent != null)
                {
                    requestContent.Value <JObject>("properties").Add("provisioningState", "Succeeded");
                    return(Request.CreateResponse(HttpStatusCode.OK, requestContent));
                }

                return(Request.CreateResponse(HttpStatusCode.OK));
            }
            catch (Exception ex)
            {
                return(ArmUtils.CreateErrorResponse(Request, HttpStatusCode.BadRequest, ex));
            }
        }