Beispiel #1
0
        private async Task <FtpSummary> DeleteFilesAsync(
            RuleConfiguration ruleConfiguration,
            ImmutableArray <FtpPath> fileSystemItems,
            CancellationToken cancellationToken)
        {
            var deploymentChangeSummary = new FtpSummary();

            foreach (var fileSystemItem in fileSystemItems
                     .Where(fileSystemItem => fileSystemItem.Type == FileSystemType.File))
            {
                if (ruleConfiguration.AppDataSkipDirectiveEnabled && fileSystemItem.IsAppDataDirectoryOrFile)
                {
                    continue;
                }

                if (ruleConfiguration.Excludes.Any(value =>
                                                   fileSystemItem.Path.StartsWith(value, StringComparison.OrdinalIgnoreCase)))
                {
                    deploymentChangeSummary.IgnoredFiles.Add(fileSystemItem.Path);
                    continue;
                }

                await DeleteFileAsync(fileSystemItem, cancellationToken);

                deploymentChangeSummary.Deleted.Add(fileSystemItem.Path);
            }

            return(deploymentChangeSummary);
        }
Beispiel #2
0
        private async Task <FtpSummary> UploadDirectoryInternalAsync(
            [NotNull] DirectoryInfo sourceDirectory,
            [NotNull] DirectoryInfo baseDirectory,
            FtpPath basePath,
            CancellationToken cancellationToken)
        {
            var dir = basePath.Append(new FtpPath(PathHelper.RelativePath(sourceDirectory, baseDirectory),
                                                  FileSystemType.Directory));

            var summary = new FtpSummary();

            bool directoryExists = await DirectoryExistsAsync(dir, cancellationToken);

            if (!directoryExists)
            {
                await CreateDirectoryAsync(dir, cancellationToken);

                summary.CreatedDirectories.Add(dir.Path);
            }

            var uploadSummary = await UploadFilesAsync(sourceDirectory, baseDirectory, basePath, cancellationToken);

            summary.Add(uploadSummary);

            return(summary);
        }
Beispiel #3
0
        private async Task <IDeploymentChangeSummary> PublishInternalAsync(
            [NotNull] RuleConfiguration ruleConfiguration,
            [NotNull] DirectoryInfo sourceDirectory,
            CancellationToken cancellationToken)
        {
            var deploymentChangeSummary = new FtpSummary();

            var stopwatch = Stopwatch.StartNew();

            try
            {
                var basePath = _ftpSettings.BasePath ?? FtpPath.Root;

                if (!await DirectoryExistsAsync(basePath, cancellationToken))
                {
                    await CreateDirectoryAsync(basePath, cancellationToken);
                }

                _logger.Debug("Listing files in remote path '{Path}'", basePath.Path);

                var fileSystemItems =
                    await ListDirectoryAsync(basePath, cancellationToken);

                var sourceFiles = sourceDirectory
                                  .GetFiles("*", SearchOption.AllDirectories)
                                  .Select(s =>
                                          basePath.Append(new FtpPath(PathHelper.RelativePath(s, sourceDirectory), FileSystemType.File)))
                                  .ToArray();

                var filesToKeep = fileSystemItems
                                  .Where(s => KeepFile(s, ruleConfiguration))
                                  .Where(s => s.Type == FileSystemType.File)
                                  .ToArray();

                var filesToRemove = fileSystemItems
                                    .Except(sourceFiles)
                                    .Except(filesToKeep)
                                    .Where(s => s.Type == FileSystemType.File)
                                    .ToImmutableArray();

                var updated = fileSystemItems.Except(filesToRemove)
                              .Where(s => s.Type == FileSystemType.File);

                if (ruleConfiguration.AppOfflineEnabled)
                {
                    using var tempFile = TempFile.CreateTempFile("App_Offline", ".htm");
                    var appOfflinePath = new FtpPath($"/{tempFile.File.Name}", FileSystemType.File);

                    var appOfflineFullPath =
                        (_ftpSettings.PublicRootPath ?? _ftpSettings.BasePath ?? FtpPath.Root).Append(
                            appOfflinePath);

                    await UploadFileAsync(appOfflineFullPath, tempFile.File, cancellationToken);

                    _logger.Debug("Uploaded file '{App_Offline}'", appOfflineFullPath.Path);
                }

                var deleteFiles = await DeleteFilesAsync(ruleConfiguration, filesToRemove, cancellationToken);

                deploymentChangeSummary.Add(deleteFiles);

                foreach (var ftpPath in filesToRemove)
                {
                    deploymentChangeSummary.Deleted.Add(ftpPath.Path);
                }

                foreach (var ftpPath in updated)
                {
                    deploymentChangeSummary.UpdatedFiles.Add(ftpPath.Path);
                }

                var uploadDirectoryAsync =
                    await UploadDirectoryAsync(ruleConfiguration,
                                               sourceDirectory,
                                               sourceDirectory,
                                               basePath,
                                               cancellationToken);

                deploymentChangeSummary.Add(uploadDirectoryAsync);

                if (ruleConfiguration.AppOfflineEnabled)
                {
                    var appOfflineFiles = sourceFiles.Intersect(fileSystemItems)
                                          .Where(file => file.Path != null &&
                                                 Path.GetFileName(file.Path)
                                                 .Equals(DeploymentConstants.AppOfflineHtm,
                                                         StringComparison.OrdinalIgnoreCase))
                                          .Select(file => file.Path)
                                          .Distinct(StringComparer.OrdinalIgnoreCase)
                                          .Select(file => new FtpPath(file, FileSystemType.File))
                                          .ToArray();

                    foreach (var appOfflineFile in appOfflineFiles)
                    {
                        bool fileExists = await FileExistsAsync(appOfflineFile, cancellationToken);

                        if (fileExists)
                        {
                            await DeleteFileAsync(appOfflineFile, cancellationToken);

                            _logger.Debug("Deleted {App_Offline}", appOfflineFile.Path);
                        }
                    }
                }
            }
            finally
            {
                stopwatch.Stop();
            }

            deploymentChangeSummary.TotalTime = stopwatch.Elapsed;

            return(deploymentChangeSummary);
        }
Beispiel #4
0
        private async Task <FtpSummary> UploadFilesAsync(
            DirectoryInfo sourceDirectory,
            DirectoryInfo baseDirectory,
            FtpPath basePath,
            CancellationToken cancellationToken)
        {
            var summary = new FtpSummary();

            string[] localPaths = sourceDirectory
                                  .GetFiles()
                                  .Select(f => f.FullName)
                                  .ToArray();

            int totalCount = localPaths.Length;

            if (totalCount == 0)
            {
                return(summary);
            }

            var relativeDir = basePath.Append(new FtpPath(PathHelper.RelativePath(sourceDirectory, baseDirectory),
                                                          FileSystemType.Directory));

            _logger.Verbose("Uploading {Files} files", localPaths.Length);

            int batchSize = _ftpSettings.BatchSize;

            var totalTime = Stopwatch.StartNew();

            try
            {
                int batches = (int)Math.Ceiling(totalCount / (double)batchSize);

                int uploaded = 0;

                for (int i = 0; i < batches; i++)
                {
                    bool batchSuccessful = false;

                    int batchNumber = i + 1;

                    string[] files = localPaths.Skip(i * batchSize).Take(batchSize).ToArray();

                    var stopwatch = new Stopwatch();

                    for (int j = 0; j < _ftpSettings.MaxAttempts; j++)
                    {
                        try
                        {
                            stopwatch.Restart();

                            int uploadedFiles = await _ftpClient.UploadFilesAsync(files,
                                                                                  relativeDir.Path,
                                                                                  FtpRemoteExists.Overwrite,
                                                                                  true,
                                                                                  FtpVerify.Delete | FtpVerify.Retry,
                                                                                  token : cancellationToken);

                            if (uploadedFiles == files.Length)
                            {
                                batchSuccessful = true;
                                break;
                            }

                            _logger.Warning(
                                "The expected number of uploaded files was {Expected} but result was {Actual}, retrying batch {Batch}",
                                files.Length,
                                uploadedFiles,
                                batchNumber);
                        }
                        catch (Exception ex) when(!ex.IsFatal())
                        {
                            _logger.Warning(ex, "FTP ERROR in batch {Batch}", batchNumber);
                        }
                        finally
                        {
                            stopwatch.Stop();
                        }
                    }

                    if (!batchSuccessful)
                    {
                        string message = batches > 1
                            ? $"The batch {batchNumber} failed"
                            : $"Failed to upload files {files}";

                        throw new InvalidOperationException(message);
                    }

                    uploaded += files.Length;
                    string elapsed = $"{stopwatch.Elapsed.TotalSeconds:F2}";

                    string percentage = $"{100 * uploaded / (double)totalCount:F1}";

                    string paddedPercentage = new string(' ', 5 - percentage.Length) + percentage;

                    double averageTime = totalTime.Elapsed.TotalSeconds / uploaded;

                    string average = averageTime.ToString("F2", CultureInfo.InvariantCulture);

                    string timeLeft = $"{(totalCount - uploaded) * averageTime:F2}s";

                    string paddedBatch =
                        $"{new string(' ', batches.ToString(CultureInfo.InvariantCulture).Length - batchNumber.ToString(CultureInfo.InvariantCulture).Length)}{batchNumber}";

                    string paddedUploaded =
                        $"{new string(' ', totalCount.ToString(CultureInfo.InvariantCulture).Length - uploaded.ToString(CultureInfo.InvariantCulture).Length)}{uploaded}";

                    string totalElapsed = totalTime.Elapsed.TotalSeconds.ToString("F2", CultureInfo.InvariantCulture);

                    if (batches > 1)
                    {
                        _logger.Information(
                            "Uploaded batch {BatchNumber} of {BatchCount} using batch size {Size}, {Uploaded}/{Total} {Percentage}%, average {Average}s per file, time left: ~{TimeLeft}, took {ElapsedTime}s, total time {TotalTime}s",
                            paddedBatch,
                            batches,
                            batchSize,
                            paddedUploaded,
                            totalCount,
                            paddedPercentage,
                            average,
                            timeLeft,
                            elapsed,
                            totalElapsed);
                    }
                    else
                    {
                        _logger.Information("Uploaded files {Files}, took {TotalElapsed}s", files, totalElapsed);
                    }
                }
            }
            finally
            {
                totalTime.Stop();
            }

            foreach (string path in localPaths)
            {
                summary.CreatedFiles.Add(PathHelper.RelativePath(new FileInfo(path), baseDirectory));
            }

            foreach (var directoryInfo in sourceDirectory.GetDirectories())
            {
                var subSummary = await UploadFilesAsync(directoryInfo, baseDirectory, basePath, cancellationToken);

                summary.Add(subSummary);
            }

            return(summary);
        }