Ejemplo n.º 1
0
        private void ExtractTriggers(IRepository repository, ArtifactDeploymentInfo deploymentInfo)
        {
            FileSystemHelpers.EnsureDirectory(deploymentInfo.SyncFunctionsTriggersPath);
            // Loading the zip file depends on how fast the file system is.
            // Tested Azure Files share with a zip containing 120k files (160 MBs)
            // takes 20 seconds to load. On my machine it takes 900 msec.
            using (var zip = ZipFile.OpenRead(Path.Combine(_environment.SitePackagesPath, deploymentInfo.ArtifactFileName)))
            {
                var entries = zip.Entries
                              // Only select host.json, proxies.json, or function.json that are from top level directories only
                              // Tested with a zip containing 120k files, and this took 90 msec
                              // on my machine.
                              .Where(e => e.FullName.Equals(Constants.FunctionsHostConfigFile, StringComparison.OrdinalIgnoreCase) ||
                                     e.FullName.Equals(Constants.ProxyConfigFile, StringComparison.OrdinalIgnoreCase) ||
                                     isFunctionJson(e.FullName));

                foreach (var entry in entries)
                {
                    var path = Path.Combine(deploymentInfo.SyncFunctionsTriggersPath, entry.FullName);
                    FileSystemHelpers.EnsureDirectory(Path.GetDirectoryName(path));
                    entry.ExtractToFile(path, overwrite: true);
                }
            }

            CommitRepo(repository, deploymentInfo);

            bool isFunctionJson(string fullName)
            {
                return(fullName.EndsWith(Constants.FunctionsConfigFile) &&
                       fullName.Count(c => c == '/' || c == '\\') == 1);
            }
        }
Ejemplo n.º 2
0
        private async Task WriteSitePackageZip(ArtifactDeploymentInfo zipDeploymentInfo, ITracer tracer)
        {
            var filePath = Path.Combine(_environment.SitePackagesPath, zipDeploymentInfo.ArtifactFileName);

            // Make sure D:\home\data\SitePackages exists
            FileSystemHelpers.EnsureDirectory(_environment.SitePackagesPath);
            using (_tracer.Step("Writing zip file to {0}", filePath))
            {
                if (HttpContext.Request.ContentType.Contains("multipart/form-data",
                                                             StringComparison.OrdinalIgnoreCase))
                {
                    FormValueProvider formModel;
                    using (_tracer.Step("Writing zip file to {0}", filePath))
                    {
                        using (var file = System.IO.File.Create(filePath))
                        {
                            formModel = await Request.StreamFile(file);
                        }
                    }
                }
                else
                {
                    using (var file = System.IO.File.Create(filePath))
                    {
                        await Request.Body.CopyToAsync(file);
                    }
                }
            }

            DeploymentHelper.PurgeBuildArtifactsIfNecessary(_environment.SitePackagesPath, BuildArtifactType.Zip,
                                                            tracer, _settings.GetMaxZipPackageCount());
        }
Ejemplo n.º 3
0
        public static async Task UpdatePackageName(ArtifactDeploymentInfo deploymentInfo, IEnvironment environment, ILogger logger)
        {
            var packageNamePath = Path.Combine(environment.SitePackagesPath, Constants.PackageNameTxt);

            logger.Log($"Updating {packageNamePath} with deployment {deploymentInfo.ArtifactFileName}");
            await FileSystemHelpers.WriteAllTextToFileAsync(packageNamePath, deploymentInfo.ArtifactFileName);
        }
Ejemplo n.º 4
0
 private static void CommitRepo(IRepository repository, ArtifactDeploymentInfo deploymentInfo)
 {
     // Needed in order for repository.GetChangeSet() to work.
     // Similar to what OneDriveHelper and DropBoxHelper do.
     // We need to make to call repository.Commit() since deployment flow expects at
     // least 1 commit in the IRepository. Even though there is no repo per se in this
     // scenario, deployment pipeline still generates a NullRepository
     repository.Commit(deploymentInfo.Message, deploymentInfo.Author, deploymentInfo.AuthorEmail);
 }
Ejemplo n.º 5
0
        public static async Task UpdateSiteVersion(ArtifactDeploymentInfo deploymentInfo, IEnvironment environment, ITracer tracer)
        {
            var siteVersionPath = Path.Combine(environment.SitePackagesPath, Constants.PackageNameTxt);

            using (tracer.Step($"Updating {siteVersionPath} with deployment {deploymentInfo.ArtifactFileName}"))
            {
                await OperationManager.AttemptAsync(() => FileSystemHelpers.WriteAllTextToFileAsync(siteVersionPath, deploymentInfo.ArtifactFileName));
            }
        }
Ejemplo n.º 6
0
        public async Task <IActionResult> ZipPushDeploy(
            [FromQuery] bool isAsync      = false,
            [FromQuery] bool syncTriggers = false,
            [FromQuery] bool overwriteWebsiteRunFromPackage = false,
            [FromQuery] string author      = null,
            [FromQuery] string authorEmail = null,
            [FromQuery] string deployer    = DefaultDeployer,
            [FromQuery] string message     = DefaultMessage)
        {
            using (_tracer.Step("ZipPushDeploy"))
            {
                string deploymentId = GetExternalDeploymentId(Request);

                var deploymentInfo = new ArtifactDeploymentInfo(_environment, _traceFactory)
                {
                    AllowDeploymentWhileScmDisabled = true,
                    Deployer                = deployer,
                    IsContinuous            = false,
                    AllowDeferredDeployment = false,
                    IsReusable              = false,
                    TargetChangeset         =
                        DeploymentManager.CreateTemporaryChangeSet(message: "Deploying from pushed zip file"),
                    CommitId             = null,
                    ExternalDeploymentId = deploymentId,
                    RepositoryType       = RepositoryType.None,
                    Fetch = LocalZipHandler,
                    DoFullBuildByDefault = false,
                    Author         = author,
                    AuthorEmail    = authorEmail,
                    Message        = message,
                    RemoteURL      = null,
                    DoSyncTriggers = syncTriggers,
                    OverwriteWebsiteRunFromPackage = overwriteWebsiteRunFromPackage && _environment.IsOnLinuxConsumption
                };

                if (_settings.RunFromLocalZip())
                {
                    // This is used if the deployment is Run-From-Zip
                    // the name of the deployed file in D:\home\data\SitePackages\{name}.zip is the
                    // timestamp in the format yyyMMddHHmmss.
                    deploymentInfo.ArtifactFileName = $"{DateTime.UtcNow.ToString("yyyyMMddHHmmss")}.zip";
                    // This is also for Run-From-Zip where we need to extract the triggers
                    // for post deployment sync triggers.
                    deploymentInfo.SyncFunctionsTriggersPath =
                        Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
                }
                ;

                return(await PushDeployAsync(deploymentInfo, isAsync, HttpContext));
            }
        }
Ejemplo n.º 7
0
        private async Task WriteSitePackageZip(ArtifactDeploymentInfo zipDeploymentInfo, ITracer tracer, HttpContent content)
        {
            var filePath = Path.Combine(_environment.SitePackagesPath, zipDeploymentInfo.ArtifactFileName);

            // Make sure D:\home\data\SitePackages exists
            FileSystemHelpers.EnsureDirectory(_environment.SitePackagesPath);

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

            DeploymentHelper.PurgeZipsIfNecessary(_environment.SitePackagesPath, tracer, _settings.GetMaxZipPackageCount());
        }
Ejemplo n.º 8
0
        private async Task <string> DeployZipLocally(ArtifactDeploymentInfo zipDeploymentInfo, ITracer tracer)
        {
            var content = await DeploymentHelper.GetArtifactContentFromURL(zipDeploymentInfo, tracer);

            var zipFileName = Path.ChangeExtension(Path.GetRandomFileName(), "zip");
            var zipFilePath = Path.Combine(_environment.ZipTempPath, zipFileName);

            using (_tracer.Step("Downloading content from {0} to {1}", zipDeploymentInfo.RemoteURL.Split('?')[0], zipFilePath))
            {
                await content.CopyToAsync(zipFilePath, _tracer);
            }

            zipDeploymentInfo.RepositoryUrl = zipFilePath;
            return(zipFilePath);
        }
Ejemplo n.º 9
0
 private async Task LocalZipHandler(IRepository repository, DeploymentInfoBase deploymentInfo, string targetBranch, ILogger logger, ITracer tracer)
 {
     if (_settings.RunFromLocalZip() && deploymentInfo is ArtifactDeploymentInfo)
     {
         ArtifactDeploymentInfo zipDeploymentInfo = (ArtifactDeploymentInfo)deploymentInfo;
         // If this was a request with a Zip URL in the JSON, we first need to get the zip content and write it to the site.
         if (!string.IsNullOrEmpty(zipDeploymentInfo.RemoteURL))
         {
             await WriteSitePackageZip(zipDeploymentInfo, tracer, await DeploymentHelper.GetArtifactContentFromURL(zipDeploymentInfo, tracer));
         }
         // If this is a Run-From-Zip deployment, then we need to extract function.json
         // from the zip file into path zipDeploymentInfo.SyncFunctionsTriggersPath
         ExtractTriggers(repository, zipDeploymentInfo);
     }
     else
     {
         await LocalZipFetch(repository, deploymentInfo, targetBranch, logger, tracer);
     }
 }
Ejemplo n.º 10
0
        public async Task <IActionResult> WarPushDeploy(
            [FromQuery] bool isAsync       = false,
            [FromQuery] string author      = null,
            [FromQuery] string authorEmail = null,
            [FromQuery] string deployer    = DefaultDeployer,
            [FromQuery] string message     = DefaultMessage)
        {
            using (_tracer.Step("WarPushDeploy"))
            {
                string deploymentId = GetExternalDeploymentId(Request);

                var appName = HttpContext.Request.Query["name"].ToString();
                if (string.IsNullOrWhiteSpace(appName))
                {
                    appName = "ROOT";
                }

                var deploymentInfo = new ArtifactDeploymentInfo(_environment, _traceFactory)
                {
                    AllowDeploymentWhileScmDisabled = true,
                    Deployer = deployer,
                    TargetSubDirectoryRelativePath = Path.Combine("webapps", appName),
                    WatchedFilePath         = Path.Combine("WEB-INF", "web.xml"),
                    IsContinuous            = false,
                    AllowDeferredDeployment = false,
                    IsReusable             = false,
                    CleanupTargetDirectory =
                        true, // For now, always cleanup the target directory. If needed, make it configurable
                    TargetChangeset =
                        DeploymentManager.CreateTemporaryChangeSet(message: "Deploying from pushed war file"),
                    CommitId             = null,
                    ExternalDeploymentId = deploymentId,
                    RepositoryType       = RepositoryType.None,
                    Fetch = LocalZipFetch,
                    DoFullBuildByDefault = false,
                    Author      = author,
                    AuthorEmail = authorEmail,
                    Message     = message,
                    RemoteURL   = null
                };
                return(await PushDeployAsync(deploymentInfo, isAsync, HttpContext));
            }
        }
Ejemplo n.º 11
0
        public async Task <HttpResponseMessage> ZipPushDeploy(
            [FromUri] bool isAsync                 = false,
            [FromUri] string author                = null,
            [FromUri] string authorEmail           = null,
            [FromUri] string deployer              = Constants.ZipDeploy,
            [FromUri] string message               = DefaultMessage,
            [FromUri] bool trackDeploymentProgress = false)
        {
            using (_tracer.Step("ZipPushDeploy"))
            {
                var deploymentInfo = new ArtifactDeploymentInfo(_environment, _traceFactory)
                {
                    AllowDeploymentWhileScmDisabled = true,
                    Deployer                = deployer,
                    IsContinuous            = false,
                    AllowDeferredDeployment = false,
                    IsReusable              = false,
                    TargetChangeset         = DeploymentManager.CreateTemporaryChangeSet(message: "Deploying from pushed zip file"),
                    CommitId                = null,
                    DeploymentTrackingId    = trackDeploymentProgress ? Guid.NewGuid().ToString() : null,
                    RepositoryType          = RepositoryType.None,
                    Fetch = LocalZipHandler,
                    DoFullBuildByDefault = false,
                    Author      = author,
                    AuthorEmail = authorEmail,
                    Message     = message
                };

                if (_settings.RunFromLocalZip())
                {
                    // This is used if the deployment is Run-From-Zip
                    // the name of the deployed file in D:\home\data\SitePackages\{name}.zip is the
                    // timestamp in the format yyyMMddHHmmss.
                    deploymentInfo.ArtifactFileName = $"{DateTime.UtcNow.ToString("yyyyMMddHHmmss")}.zip";

                    // This is also for Run-From-Zip where we need to extract the triggers
                    // for post deployment sync triggers.
                    deploymentInfo.SyncFunctionsTriggersPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
                }

                return(await PushDeployAsync(deploymentInfo, isAsync));
            }
        }
Ejemplo n.º 12
0
        public async Task <HttpResponseMessage> WarPushDeploy(
            [FromUri] bool isAsync                 = false,
            [FromUri] string author                = null,
            [FromUri] string authorEmail           = null,
            [FromUri] string deployer              = Constants.WarDeploy,
            [FromUri] string message               = DefaultMessage,
            [FromUri] bool trackDeploymentProgress = false)
        {
            using (_tracer.Step("WarPushDeploy"))
            {
                var appName = Request.RequestUri.ParseQueryString()["name"];

                if (string.IsNullOrWhiteSpace(appName))
                {
                    appName = "ROOT";
                }

                var deploymentInfo = new ArtifactDeploymentInfo(_environment, _traceFactory)
                {
                    AllowDeploymentWhileScmDisabled = true,
                    Deployer = deployer,
                    TargetSubDirectoryRelativePath = Path.Combine("webapps", appName),
                    WatchedFilePath         = Path.Combine("WEB-INF", "web.xml"),
                    IsContinuous            = false,
                    AllowDeferredDeployment = false,
                    IsReusable             = false,
                    CleanupTargetDirectory = true, // For now, always cleanup the target directory. If needed, make it configurable
                    TargetChangeset        = DeploymentManager.CreateTemporaryChangeSet(message: "Deploying from pushed war file"),
                    CommitId             = null,
                    DeploymentTrackingId = trackDeploymentProgress ? Guid.NewGuid().ToString() : null,
                    RepositoryType       = RepositoryType.None,
                    Fetch = LocalZipFetch,
                    DoFullBuildByDefault = false,
                    Author      = author,
                    AuthorEmail = authorEmail,
                    Message     = message
                };

                return(await PushDeployAsync(deploymentInfo, isAsync));
            }
        }
Ejemplo n.º 13
0
        public async Task <IActionResult> ZipPushDeployViaUrl(
            [FromBody] JObject requestJson,
            [FromQuery] bool isAsync      = false,
            [FromQuery] bool syncTriggers = false,
            [FromQuery] bool overwriteWebsiteRunFromPackage = false,
            [FromQuery] string author      = null,
            [FromQuery] string authorEmail = null,
            [FromQuery] string deployer    = DefaultDeployer,
            [FromQuery] string message     = DefaultMessage)
        {
            using (_tracer.Step("ZipPushDeployViaUrl"))
            {
                string deploymentId = GetExternalDeploymentId(Request);

                string zipUrl = GetArtifactURLFromJSON(requestJson);

                var deploymentInfo = new ArtifactDeploymentInfo(_environment, _traceFactory)
                {
                    AllowDeploymentWhileScmDisabled = true,
                    Deployer                = deployer,
                    IsContinuous            = false,
                    AllowDeferredDeployment = false,
                    IsReusable              = false,
                    TargetChangeset         =
                        DeploymentManager.CreateTemporaryChangeSet(message: "Deploying from pushed zip file"),
                    CommitId             = null,
                    ExternalDeploymentId = deploymentId,
                    RepositoryType       = RepositoryType.None,
                    Fetch = LocalZipHandler,
                    DoFullBuildByDefault = false,
                    Author         = author,
                    AuthorEmail    = authorEmail,
                    Message        = message,
                    RemoteURL      = zipUrl,
                    DoSyncTriggers = syncTriggers,
                    OverwriteWebsiteRunFromPackage = overwriteWebsiteRunFromPackage && _environment.IsOnLinuxConsumption
                };
                return(await PushDeployAsync(deploymentInfo, isAsync, HttpContext));
            }
        }
Ejemplo n.º 14
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, Request.GetRequestUri(), "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(Request.GetRequestUri(),
                                                        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);
        }
Ejemplo n.º 15
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));
            }
        }
Ejemplo n.º 16
0
        private async Task <IActionResult> PushDeployAsync(ArtifactDeploymentInfo deploymentInfo, bool isAsync,
                                                           HttpContext context)
        {
            string artifactTempPath;

            if (string.IsNullOrWhiteSpace(deploymentInfo.TargetFileName))
            {
                artifactTempPath = Path.Combine(_environment.ZipTempPath, Guid.NewGuid() + ".zip");
            }
            else
            {
                artifactTempPath = Path.Combine(_environment.ZipTempPath, deploymentInfo.TargetFileName);
            }

            if (_settings.RunFromLocalZip())
            {
                await WriteSitePackageZip(deploymentInfo, _tracer);
            }
            else
            {
                var oryxManifestFile = Path.Combine(_environment.WebRootPath, "oryx-manifest.toml");
                if (FileSystemHelpers.FileExists(oryxManifestFile))
                {
                    _tracer.Step("Removing previous build artifact's manifest file");
                    FileSystemHelpers.DeleteFileSafe(oryxManifestFile);
                }

                try
                {
                    var nodeModulesSymlinkFile       = Path.Combine(_environment.WebRootPath, "node_modules");
                    Mono.Unix.UnixSymbolicLinkInfo i = new Mono.Unix.UnixSymbolicLinkInfo(nodeModulesSymlinkFile);
                    if (i.FileType == Mono.Unix.FileTypes.SymbolicLink)
                    {
                        _tracer.Step("Removing node_modules symlink");
                        // TODO: Add support to remove Unix Symlink File in DeleteFileSafe
                        // FileSystemHelpers.DeleteFileSafe(nodeModulesSymlinkFile);
                        FileSystemHelpers.RemoveUnixSymlink(nodeModulesSymlinkFile, TimeSpan.FromSeconds(5));
                    }
                }
                catch (Exception)
                {
                    // best effort
                }

                using (_tracer.Step("Writing artifact to {0}", artifactTempPath))
                {
                    if (!string.IsNullOrEmpty(context.Request.ContentType) &&
                        context.Request.ContentType.Contains("multipart/form-data", StringComparison.OrdinalIgnoreCase))
                    {
                        FormValueProvider formModel;
                        using (_tracer.Step("Writing zip file to {0}", artifactTempPath))
                        {
                            using (var file = System.IO.File.Create(artifactTempPath))
                            {
                                formModel = await Request.StreamFile(file);
                            }
                        }
                    }
                    else if (deploymentInfo.RemoteURL != null)
                    {
                        using (_tracer.Step("Writing zip file from packageUri to {0}", artifactTempPath))
                        {
                            using (var httpClient = new HttpClient())
                                using (var fileStream = new FileStream(artifactTempPath,
                                                                       FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true))
                                {
                                    var zipUrlRequest  = new HttpRequestMessage(HttpMethod.Get, deploymentInfo.RemoteURL);
                                    var zipUrlResponse = await httpClient.SendAsync(zipUrlRequest);

                                    try
                                    {
                                        zipUrlResponse.EnsureSuccessStatusCode();
                                    }
                                    catch (HttpRequestException hre)
                                    {
                                        _tracer.TraceError(hre, "Failed to get file from packageUri {0}", deploymentInfo.RemoteURL);
                                        throw;
                                    }

                                    using (var content = await zipUrlResponse.Content.ReadAsStreamAsync())
                                    {
                                        await content.CopyToAsync(fileStream);
                                    }
                                }
                        }
                    }
                    else
                    {
                        using (var file = System.IO.File.Create(artifactTempPath))
                        {
                            await Request.Body.CopyToAsync(file);
                        }
                    }

                    deploymentInfo.RepositoryUrl = artifactTempPath;
                }
            }

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

            switch (result)
            {
            case FetchDeploymentRequestResult.RunningAynschronously:
                if (isAsync)
                {
                    // latest deployment keyword reserved to poll till deployment done
                    Response.GetTypedHeaders().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")));
                }

                return(Accepted());

            case FetchDeploymentRequestResult.ForbiddenScmDisabled:
                // Should never hit this for zip push deploy
                _tracer.Trace("Scm is not enabled, reject all requests.");
                return(Forbid());

            case FetchDeploymentRequestResult.ConflictAutoSwapOngoing:
                return(StatusCode(StatusCodes.Status409Conflict, Resources.Error_AutoSwapDeploymentOngoing));

            case FetchDeploymentRequestResult.Pending:
                // Shouldn't happen here, as we disallow deferral for this use case
                return(Accepted());

            case FetchDeploymentRequestResult.RanSynchronously:
                return(Ok());

            case FetchDeploymentRequestResult.ConflictDeploymentInProgress:
                return(StatusCode(StatusCodes.Status409Conflict, Resources.Error_DeploymentInProgress));

            case FetchDeploymentRequestResult.ConflictRunFromRemoteZipConfigured:
                return(StatusCode(StatusCodes.Status409Conflict, Resources.Error_RunFromRemoteZipConfigured));

            default:
                return(BadRequest());
            }
        }
Ejemplo n.º 17
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));
            }
        }
        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));
            }
        }