Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Large diffs are not rendered by default.

72 changes: 72 additions & 0 deletions source/Calamari/Commands/DownloadAndRegisterPackageCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;
using Calamari.Commands.Support;
using Calamari.Common.Commands;
using Calamari.Common.Features.Packages;
using Calamari.Common.Features.Processes;
using Calamari.Common.Features.Scripting;
using Calamari.Common.Plumbing.Deployment.PackageRetention;
using Calamari.Common.Plumbing.FileSystem;
using Calamari.Common.Plumbing.Logging;
using Calamari.Common.Plumbing.Variables;
using Octopus.Versioning;

namespace Calamari.Commands
{
[Command("download-and-register-package",
Description = "Downloads a package and atomically registers its use in the package journal")]
public class DownloadAndRegisterPackageCommand : Command
{
readonly ILog log;
readonly IManagePackageCache journal;
readonly PackageDownloadService downloadService;
readonly PackageDownloadAndRegisterOptions options;

public DownloadAndRegisterPackageCommand(
IScriptEngine scriptEngine,
IVariables variables,
ICalamariFileSystem fileSystem,
ICommandLineRunner commandLineRunner,
ILog log,
IManagePackageCache journal)
{
this.log = log;
this.journal = journal;
this.downloadService = new PackageDownloadService(scriptEngine, variables, fileSystem, commandLineRunner, log);
this.options = new PackageDownloadAndRegisterOptions();
PackageDownloadAndRegisterOptions.ConfigureOptions(Options, options);
}

public override int Execute(string[] commandLineArguments)
{
Options.Parse(commandLineArguments);

try
{
var pkg = downloadService.DownloadPackageForRegistration(options);
var version = VersionFactory.TryCreateVersion(options.PackageVersion, options.VersionFormat);
RegisterPackageUse(pkg, version);
return 0;
}
catch (Exception ex)
{
log.ErrorFormat("Failed to download and register package {0} v{1} from feed: '{2}'",
options.PackageId, options.PackageVersion, options.FeedUri);
return ConsoleFormatter.PrintError(log, ex);
}
}

void RegisterPackageUse(PackagePhysicalFileMetadata pkg, IVersion version)
{
if (string.IsNullOrEmpty(pkg.FullFilePath))
return;

var package = new PackageIdentity(
new PackageId(pkg.PackageId),
version,
new PackagePath(pkg.FullFilePath));
var size = (ulong)pkg.Size;
journal.RegisterPackageUse(package, new ServerTaskId(options.TaskId), size);
}

}
}
155 changes: 8 additions & 147 deletions source/Calamari/Commands/DownloadPackageCommand.cs
Original file line number Diff line number Diff line change
@@ -1,41 +1,22 @@
using System;
using System.Diagnostics;
using System.Globalization;
using System.Threading;
using Calamari.Commands.Support;
using Calamari.Common.Commands;
using Calamari.Common.Features.Packages;
using Calamari.Common.Features.Processes;
using Calamari.Common.Features.Scripting;
using Calamari.Common.Plumbing;
using Calamari.Common.Plumbing.FileSystem;
using Calamari.Common.Plumbing.Logging;
using Calamari.Common.Plumbing.Variables;
using Calamari.Integration.Packages.Download;
using Octopus.Versioning;

namespace Calamari.Commands
{
[Command("download-package", Description = "Downloads a NuGet package from a NuGet feed")]
public class DownloadPackageCommand : Command
{
private readonly IScriptEngine scriptEngine;
readonly IVariables variables;
readonly ICalamariFileSystem fileSystem;
readonly ILog log;
readonly ICommandLineRunner commandLineRunner;

string packageId;
string packageVersion;
bool forcePackageDownload;
string feedId;
string feedUri;
string feedUsername;
string feedPassword;
string maxDownloadAttempts = "5";
string attemptBackoffSeconds = "10";
private FeedType feedType = FeedType.NuGet;
private VersionFormat versionFormat = VersionFormat.Semver;
readonly PackageDownloadService downloadService;
readonly PackageDownloadOptions options;

public DownloadPackageCommand(
IScriptEngine scriptEngine,
Expand All @@ -44,151 +25,31 @@ public DownloadPackageCommand(
ICommandLineRunner commandLineRunner,
ILog log)
{
this.scriptEngine = scriptEngine;
this.variables = variables;
this.fileSystem = fileSystem;
this.log = log;
this.commandLineRunner = commandLineRunner;
Options.Add("packageId=", "Package ID to download", v => packageId = v);
Options.Add("packageVersion=", "Package version to download", v => packageVersion = v);
Options.Add("packageVersionFormat=", $"[Optional] Format of version. Options {string.Join(", ", Enum.GetNames(typeof(VersionFormat)))}. Defaults to `{VersionFormat.Semver}`.",
v =>
{
if (!Enum.TryParse(v, out VersionFormat format))
{
throw new CommandException($"The provided version format `{format}` is not recognised.");
}
versionFormat = format;
});
Options.Add("feedId=", "Id of the feed", v => feedId = v);
Options.Add("feedUri=", "URL to feed", v => feedUri = v);
Options.Add("feedUsername=", "[Optional] Username to use for an authenticated feed", v => feedUsername = v);
Options.Add("feedPassword=", "[Optional] Password to use for an authenticated feed", v => feedPassword = v);
Options.Add("feedType=", $"[Optional] Type of feed. Options {string.Join(", ", Enum.GetNames(typeof(FeedType)))}. Defaults to `{FeedType.NuGet}`.",
v =>
{
if (!Enum.TryParse(v, out FeedType type))
{
throw new CommandException($"The provided feed type `{type}` is not recognised.");
}
this.downloadService = new PackageDownloadService(scriptEngine, variables, fileSystem, commandLineRunner, log);
this.options = new PackageDownloadOptions();

feedType = type;
});
Options.Add("attempts=", $"[Optional] The number of times to attempt downloading the package. Default: {maxDownloadAttempts}", v => maxDownloadAttempts = v);
Options.Add("attemptBackoffSeconds=", $"[Optional] The number of seconds to apply as a linear backoff between each download attempt. Default: {attemptBackoffSeconds}", v => attemptBackoffSeconds = v);
Options.Add("forcePackageDownload", "[Optional, Flag] if specified, the package will be downloaded even if it is already in the package cache", v => forcePackageDownload = true);
PackageDownloadOptions.ConfigureOptions(Options, options);
}


public override int Execute(string[] commandLineArguments)
{
Options.Parse(commandLineArguments);

// Add Feed Type so we can tell which auth to use when downloading
variables.Set(AuthenticationVariables.FeedType, feedType.ToString());

try
{
CheckArguments(
packageId,
packageVersion,
feedId,
feedUri,
feedUsername,
feedPassword,
maxDownloadAttempts,
attemptBackoffSeconds,
out var version,
out var uri,
out var parsedMaxDownloadAttempts,
out var parsedAttemptBackoff);

var packageDownloaderStrategy = new PackageDownloaderStrategy(log,
scriptEngine,
fileSystem,
commandLineRunner,
variables);

var pkg = packageDownloaderStrategy.DownloadPackage(
packageId,
version,
feedId,
uri,
feedType,
feedUsername,
feedPassword,
forcePackageDownload,
parsedMaxDownloadAttempts,
parsedAttemptBackoff);

log.VerboseFormat("Package {0} v{1} successfully downloaded from feed: '{2}'", packageId, version, feedUri);
log.SetOutputVariableButDoNotAddToVariables("StagedPackage.Hash", pkg.Hash);
log.SetOutputVariableButDoNotAddToVariables("StagedPackage.Size", pkg.Size.ToString(CultureInfo.InvariantCulture));
log.SetOutputVariableButDoNotAddToVariables("StagedPackage.FullPathOnRemoteMachine", pkg.FullFilePath);
downloadService.DownloadPackage(options);
}
catch (Exception ex)
{
log.ErrorFormat("Failed to download package {0} v{1} from feed: '{2}'", packageId, packageVersion, feedUri);
log.ErrorFormat("Failed to download package {0} v{1} from feed: '{2}'",
options.PackageId, options.PackageVersion, options.FeedUri);
return ConsoleFormatter.PrintError(log, ex);
}

return 0;
}

// ReSharper disable UnusedParameter.Local
void CheckArguments(
string packageId,
string packageVersion,
string feedId,
string feedUri,
string feedUsername,
string feedPassword,
string maxDownloadAttempts,
string attemptBackoffSeconds,
out IVersion version,
out Uri uri,
out int parsedMaxDownloadAttempts,
out TimeSpan parsedAttemptBackoff)
{
Guard.NotNullOrWhiteSpace(packageId, "No package ID was specified. Please pass --packageId YourPackage");
Guard.NotNullOrWhiteSpace(packageVersion, "No package version was specified. Please pass --packageVersion 1.0.0.0");
Guard.NotNullOrWhiteSpace(feedId, "No feed ID was specified. Please pass --feedId feed-id");

var usingOidc = !string.IsNullOrWhiteSpace(variables.Get("Jwt"));
if (feedType != FeedType.S3 && feedType != FeedType.AwsElasticContainerRegistry)
{
Guard.NotNullOrWhiteSpace(feedUri, "No feed URI was specified. Please pass --feedUri https://url/to/nuget/feed");
}

version = VersionFactory.TryCreateVersion(packageVersion, versionFormat);
if (version == null)
{
throw new CommandException($"Package version '{packageVersion}' specified is not a valid {versionFormat.ToString()} version string");
}

if (feedType == FeedType.S3 || feedType == FeedType.AwsElasticContainerRegistry)
{
uri = null;
}
else if (!Uri.TryCreate(feedUri, UriKind.Absolute, out uri))
throw new CommandException($"URI specified '{feedUri}' is not a valid URI");

if (!String.IsNullOrWhiteSpace(feedUsername) && String.IsNullOrWhiteSpace(feedPassword) && !usingOidc)
throw new CommandException("A username was specified but no password was provided. Please pass --feedPassword \"FeedPassword\"");

if (!int.TryParse(maxDownloadAttempts, out parsedMaxDownloadAttempts))
throw new CommandException($"The requested number of download attempts '{maxDownloadAttempts}' is not a valid integer number");

if (parsedMaxDownloadAttempts <= 0)
throw new CommandException("The requested number of download attempts should be more than zero");

if (!int.TryParse(attemptBackoffSeconds, out var parsedAttemptBackoffSeconds))
throw new CommandException($"Retry requested download attempt retry backoff '{attemptBackoffSeconds}' is not a valid integer number of seconds");

if (parsedAttemptBackoffSeconds < 0)
throw new CommandException("The requested download attempt retry backoff should be a positive integer number of seconds");

parsedAttemptBackoff = TimeSpan.FromSeconds(parsedAttemptBackoffSeconds);
}
}
}
75 changes: 75 additions & 0 deletions source/Calamari/Commands/FindAndRegisterPackageCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System;
using Calamari.Commands.Support;
using Calamari.Common.Commands;
using Calamari.Common.Features.Packages;
using Calamari.Common.Plumbing.Deployment.PackageRetention;
using Calamari.Common.Plumbing.FileSystem;
using Calamari.Common.Plumbing.Logging;
using Calamari.Integration.FileSystem;
using Octopus.Versioning;

namespace Calamari.Commands
{
[Command("find-and-register-package",
Description = "Finds a package and atomically registers its use in the package journal")]
public class FindAndRegisterPackageCommand : Command
{
readonly ILog log;
readonly IManagePackageCache journal;
readonly ICalamariFileSystem fileSystem;
readonly PackageFindService findService;
readonly PackageFindRegistrationOptions options;

public FindAndRegisterPackageCommand(
ILog log,
IPackageStore packageStore,
IManagePackageCache journal,
ICalamariFileSystem fileSystem)
{
this.log = log;
this.journal = journal;
this.fileSystem = fileSystem;
this.findService = new PackageFindService(log, packageStore);
this.options = new PackageFindRegistrationOptions();

PackageFindRegistrationOptions.ConfigureOptions(Options, options);
}

public override int Execute(string[] commandLineArguments)
{
Options.Parse(commandLineArguments);

try
{
var package = findService.FindPackageForRegistration(options);

if (package != null)
{
var version = VersionFactory.TryCreateVersion(options.PackageVersion, options.VersionFormat);
RegisterPackageUse(package, version);
}

return 0;
}
catch (Exception ex)
{
log.ErrorFormat("Failed to find and register package {0} v{1} hash {2}",
options.PackageId, options.PackageVersion, options.PackageHash);
return ConsoleFormatter.PrintError(log, ex);
}
}

void RegisterPackageUse(PackagePhysicalFileMetadata pkg, IVersion version)
{
if (string.IsNullOrEmpty(pkg.FullFilePath))
return;

var package = new PackageIdentity(
new PackageId(pkg.PackageId),
version,
new PackagePath(pkg.FullFilePath));
var size = fileSystem.GetFileSize(package.Path.Value);
journal.RegisterPackageUse(package, new ServerTaskId(options.TaskId), (ulong)size);
}
}
}
Loading