|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Build.Framework;
using Microsoft.NET.Build.Containers.Resources;
namespace Microsoft.NET.Build.Containers.Tasks;
public sealed class ParseContainerProperties : Microsoft.Build.Utilities.Task
{
/// <summary>
/// The full base image name. mcr.microsoft.com/dotnet/runtime:6.0, for example.
/// </summary>
[Required]
public string FullyQualifiedBaseImageName { get; set; }
/// <summary>
/// The registry to push the new container to. This will be null if the container is to be pushed to a local registry.
/// </summary>
public string ContainerRegistry { get; set; }
/// <summary>
/// The name of the container to be created.
/// </summary>
[Required]
public string ContainerRepository { get; set; }
/// <summary>
/// The tag for the container to be created.
/// </summary>
public string ContainerImageTag { get; set; }
/// <summary>
/// The tags for the container to be created.
/// </summary>
public string[] ContainerImageTags { get; set; }
/// <summary>
/// Container environment variables to set.
/// </summary>
public ITaskItem[] ContainerEnvironmentVariables { get; set; }
[Output]
public string ParsedContainerRegistry { get; private set; }
[Output]
public string ParsedContainerImage { get; private set; }
[Output]
public string ParsedContainerTag { get; private set; }
[Output]
public string ParsedContainerDigest { get; private set; }
[Output]
public string NewContainerRegistry { get; private set; }
[Output]
public string NewContainerRepository { get; private set; }
[Output]
public string[] NewContainerTags { get; private set; }
[Output]
public ITaskItem[] NewContainerEnvironmentVariables { get; private set; }
public ParseContainerProperties()
{
FullyQualifiedBaseImageName = "";
ContainerRegistry = "";
ContainerRepository = "";
ContainerImageTag = "";
ContainerImageTags = Array.Empty<string>();
ContainerEnvironmentVariables = Array.Empty<ITaskItem>();
ParsedContainerRegistry = "";
ParsedContainerImage = "";
ParsedContainerTag = "";
ParsedContainerDigest = "";
NewContainerRegistry = "";
NewContainerRepository = "";
NewContainerTags = Array.Empty<string>();
NewContainerEnvironmentVariables = Array.Empty<ITaskItem>();
TaskResources = Resource.Manager;
}
public override bool Execute()
{
string[] validTags;
if (!string.IsNullOrEmpty(ContainerImageTag) && ContainerImageTags.Length >= 1)
{
Log.LogErrorWithCodeFromResources(nameof(Strings.AmbiguousTags), nameof(ContainerImageTag), nameof(ContainerImageTags));
return !Log.HasLoggedErrors;
}
if (!string.IsNullOrEmpty(ContainerImageTag))
{
if (ContainerHelpers.IsValidImageTag(ContainerImageTag))
{
validTags = new[] { ContainerImageTag };
}
else
{
validTags = Array.Empty<string>();
Log.LogErrorWithCodeFromResources(nameof(Strings.InvalidTag), nameof(ContainerImageTag), ContainerImageTag);
}
}
else if (ContainerImageTags.Length != 0)
{
(validTags, var invalidTags) = TryValidateTags(ContainerImageTags);
if (invalidTags.Any())
{
Log.LogErrorWithCodeFromResources(nameof(Strings.InvalidTags), nameof(ContainerImageTags), String.Join(",", invalidTags));
return !Log.HasLoggedErrors;
}
}
else
{
validTags = Array.Empty<string>();
}
if (!string.IsNullOrEmpty(ContainerRegistry) && !ContainerHelpers.IsValidRegistry(ContainerRegistry))
{
Log.LogErrorWithCodeFromResources(nameof(Strings.CouldntRecognizeRegistry), ContainerRegistry);
return !Log.HasLoggedErrors;
}
ValidateEnvironmentVariables();
if (FullyQualifiedBaseImageName.Contains(' ') && BuildEngine != null)
{
Log.LogWarningWithCodeFromResources(nameof(Strings.BaseImageNameWithSpaces), nameof(FullyQualifiedBaseImageName));
}
FullyQualifiedBaseImageName = FullyQualifiedBaseImageName.Replace(' ', '-');
if (!ContainerHelpers.TryParseFullyQualifiedContainerName(FullyQualifiedBaseImageName,
out string? outputReg,
out string? outputImage,
out string? outputTag,
out string? outputDigest,
out bool isRegistrySpecified))
{
Log.LogErrorWithCodeFromResources(nameof(Strings.BaseImageNameParsingFailed), nameof(FullyQualifiedBaseImageName), FullyQualifiedBaseImageName);
return !Log.HasLoggedErrors;
}
if (!isRegistrySpecified)
{
Log.LogWarningWithCodeFromResources(nameof(Strings.BaseImageNameRegistryFallback), nameof(FullyQualifiedBaseImageName), ContainerHelpers.DockerRegistryAlias);
}
var (normalizedRepository, normalizationWarning, normalizationError) = ContainerHelpers.NormalizeRepository(ContainerRepository);
if (normalizedRepository is not null)
{
NewContainerRepository = normalizedRepository;
}
if (normalizationWarning is (string warningMessageKey, object[] warningParams))
{
Log.LogMessageFromResources(warningMessageKey, warningParams);
}
if (normalizationError is (string errorMessageKey, object[] errorParams))
{
Log.LogErrorWithCodeFromResources(errorMessageKey, errorParams);
return !Log.HasLoggedErrors;
}
ParsedContainerRegistry = outputReg ?? "";
ParsedContainerImage = outputImage ?? "";
ParsedContainerTag = outputTag ?? "";
ParsedContainerDigest = outputDigest ?? "";
NewContainerRegistry = ContainerRegistry;
NewContainerTags = validTags;
if (BuildEngine != null)
{
Log.LogMessage(MessageImportance.Low, "Parsed the following properties. Note: Spaces are replaced with dashes.");
Log.LogMessage(MessageImportance.Low, "Host: {0}", ParsedContainerRegistry);
Log.LogMessage(MessageImportance.Low, "Image: {0}", ParsedContainerImage);
Log.LogMessage(MessageImportance.Low, "Tag: {0}", ParsedContainerTag);
Log.LogMessage(MessageImportance.Low, "Digest: {0}", ParsedContainerDigest);
Log.LogMessage(MessageImportance.Low, "Image Name: {0}", NewContainerRepository);
Log.LogMessage(MessageImportance.Low, "Image Tags: {0}", string.Join(", ", NewContainerTags));
}
return !Log.HasLoggedErrors;
}
private void ValidateEnvironmentVariables()
{
var filteredEnvVars = ContainerEnvironmentVariables.Where((x) => ContainerHelpers.IsValidEnvironmentVariable(x.ItemSpec)).ToArray<ITaskItem>();
var badEnvVars = ContainerEnvironmentVariables.Where((x) => !ContainerHelpers.IsValidEnvironmentVariable(x.ItemSpec));
foreach (var badEnvVar in badEnvVars)
{
if (BuildEngine != null)
{
Log.LogWarningWithCodeFromResources(nameof(Strings.InvalidEnvVar), nameof(ContainerEnvironmentVariables), badEnvVar.ItemSpec);
}
}
NewContainerEnvironmentVariables = new ITaskItem[filteredEnvVars.Length];
for (int i = 0; i < filteredEnvVars.Length; i++)
{
NewContainerEnvironmentVariables[i] = filteredEnvVars[i];
}
}
private static (string[] validTags, string[] invalidTags) TryValidateTags(string[] inputTags)
{
var v = new List<string>();
var i = new List<string>();
foreach (var tag in inputTags)
{
if (ContainerHelpers.IsValidImageTag(tag))
{
v.Add(tag);
}
else
{
i.Add(tag);
}
}
return (v.ToArray(), i.ToArray());
}
}
|