Skip to content

Commit 6eeea6c

Browse files
github-actions[bot]BeyondEvilbaronfel
authored
[release/9.0.1xx] Backport "Pin Base Image via Digest" to release/9.0.2xx (#45869)
Co-authored-by: Jim Brännlund <[email protected]> Co-authored-by: Chet Husk <[email protected]>
1 parent c7b56da commit 6eeea6c

File tree

16 files changed

+100
-46
lines changed

16 files changed

+100
-46
lines changed

src/Containers/Microsoft.NET.Build.Containers/ContainerBuilder.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ internal static async Task<int> ContainerizeAsync(
1414
string baseRegistry,
1515
string baseImageName,
1616
string baseImageTag,
17+
string? baseImageDigest,
1718
string[] entrypoint,
1819
string[] entrypointArgs,
1920
string[] defaultArgs,
@@ -47,7 +48,7 @@ internal static async Task<int> ContainerizeAsync(
4748
bool isLocalPull = string.IsNullOrEmpty(baseRegistry);
4849
RegistryMode sourceRegistryMode = baseRegistry.Equals(outputRegistry, StringComparison.InvariantCultureIgnoreCase) ? RegistryMode.PullFromOutput : RegistryMode.Pull;
4950
Registry? sourceRegistry = isLocalPull ? null : new Registry(baseRegistry, logger, sourceRegistryMode);
50-
SourceImageReference sourceImageReference = new(sourceRegistry, baseImageName, baseImageTag);
51+
SourceImageReference sourceImageReference = new(sourceRegistry, baseImageName, baseImageTag, baseImageDigest);
5152

5253
DestinationImageReference destinationImageReference = DestinationImageReference.CreateFromSettings(
5354
imageName,
@@ -65,14 +66,14 @@ internal static async Task<int> ContainerizeAsync(
6566
var ridGraphPicker = new RidGraphManifestPicker(ridGraphPath);
6667
imageBuilder = await registry.GetImageManifestAsync(
6768
baseImageName,
68-
baseImageTag,
69+
sourceImageReference.Reference,
6970
containerRuntimeIdentifier,
7071
ridGraphPicker,
7172
cancellationToken).ConfigureAwait(false);
7273
}
7374
catch (RepositoryNotFoundException)
7475
{
75-
logger.LogError(Resource.FormatString(nameof(Strings.RepositoryNotFound), baseImageName, baseImageTag, registry.RegistryName));
76+
logger.LogError(Resource.FormatString(nameof(Strings.RepositoryNotFound), baseImageName, baseImageTag, baseImageDigest, registry.RegistryName));
7677
return 1;
7778
}
7879
catch (UnableToAccessRepositoryException)

src/Containers/Microsoft.NET.Build.Containers/KnownStrings.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public static class Properties
3131
public static readonly string ContainerBaseRegistry = nameof(ContainerBaseRegistry);
3232
public static readonly string ContainerBaseName = nameof(ContainerBaseName);
3333
public static readonly string ContainerBaseTag = nameof(ContainerBaseTag);
34+
public static readonly string ContainerBaseDigest = nameof(ContainerBaseDigest);
3435

3536
public static readonly string ContainerGenerateLabels = nameof(ContainerGenerateLabels);
3637

src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net472/PublicAPI.Unshipped.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ Microsoft.NET.Build.Containers.Tasks.CreateNewImage.BaseImageName.get -> string!
2929
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.BaseImageName.set -> void
3030
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.BaseImageTag.get -> string!
3131
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.BaseImageTag.set -> void
32+
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.BaseImageDigest.get -> string!
33+
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.BaseImageDigest.set -> void
3234
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.BaseRegistry.get -> string!
3335
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.BaseRegistry.set -> void
3436
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.ContainerEnvironmentVariables.get -> Microsoft.Build.Framework.ITaskItem![]!
@@ -113,6 +115,7 @@ Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.ParseContainerProp
113115
Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.ParsedContainerImage.get -> string!
114116
Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.ParsedContainerRegistry.get -> string!
115117
Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.ParsedContainerTag.get -> string!
118+
Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.ParsedContainerDigest.get -> string!
116119
override Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.Execute() -> bool
117120
static Microsoft.NET.Build.Containers.ContainerHelpers.TryParsePort(string! input, out Microsoft.NET.Build.Containers.Port? port, out Microsoft.NET.Build.Containers.ContainerHelpers.ParsePortError? error) -> bool
118121
static Microsoft.NET.Build.Containers.ContainerHelpers.TryParsePort(string? portNumber, string? portType, out Microsoft.NET.Build.Containers.Port? port, out Microsoft.NET.Build.Containers.ContainerHelpers.ParsePortError? error) -> bool
@@ -136,4 +139,4 @@ Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.SdkVersion.get
136139
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.SdkVersion.set -> void
137140
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.TargetFrameworkVersion.get -> string!
138141
Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.TargetFrameworkVersion.set -> void
139-
override Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.Execute() -> bool
142+
override Microsoft.NET.Build.Containers.Tasks.ComputeDotnetBaseImageAndTag.Execute() -> bool

src/Containers/Microsoft.NET.Build.Containers/PublicAPI/net9.0/PublicAPI.Unshipped.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ Microsoft.NET.Build.Containers.Tasks.CreateNewImage.BaseImageName.get -> string!
176176
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.BaseImageName.set -> void
177177
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.BaseImageTag.get -> string!
178178
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.BaseImageTag.set -> void
179+
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.BaseImageDigest.get -> string!
180+
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.BaseImageDigest.set -> void
179181
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.BaseRegistry.get -> string!
180182
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.BaseRegistry.set -> void
181183
Microsoft.NET.Build.Containers.Tasks.CreateNewImage.Cancel() -> void
@@ -262,6 +264,7 @@ Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.ParseContainerProp
262264
Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.ParsedContainerImage.get -> string!
263265
Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.ParsedContainerRegistry.get -> string!
264266
Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.ParsedContainerTag.get -> string!
267+
Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.ParsedContainerDigest.get -> string!
265268
override Microsoft.NET.Build.Containers.Tasks.CreateNewImage.Execute() -> bool
266269
override Microsoft.NET.Build.Containers.Tasks.ParseContainerProperties.Execute() -> bool
267270
static Microsoft.NET.Build.Containers.ContainerHelpers.TryParsePort(string! input, out Microsoft.NET.Build.Containers.Port? port, out Microsoft.NET.Build.Containers.ContainerHelpers.ParsePortError? error) -> bool
@@ -321,4 +324,4 @@ static Microsoft.NET.Build.Containers.ImageIndexV1.operator ==(Microsoft.NET.Bui
321324
override Microsoft.NET.Build.Containers.ImageIndexV1.GetHashCode() -> int
322325
~override Microsoft.NET.Build.Containers.ImageIndexV1.Equals(object obj) -> bool
323326
Microsoft.NET.Build.Containers.ImageIndexV1.Equals(Microsoft.NET.Build.Containers.ImageIndexV1 other) -> bool
324-
Microsoft.NET.Build.Containers.ImageIndexV1.Deconstruct(out int schemaVersion, out string! mediaType, out Microsoft.NET.Build.Containers.PlatformSpecificOciManifest[]! manifests) -> void
327+
Microsoft.NET.Build.Containers.ImageIndexV1.Deconstruct(out int schemaVersion, out string! mediaType, out Microsoft.NET.Build.Containers.PlatformSpecificOciManifest[]! manifests) -> void

src/Containers/Microsoft.NET.Build.Containers/SourceImageReference.cs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,35 @@
44
namespace Microsoft.NET.Build.Containers;
55

66
/// <summary>
7-
/// Represents a reference to a Docker image. A reference is made of a registry, a repository (aka the image name) and a tag.
7+
/// Represents a reference to a Docker image. A reference is made of a registry, a repository (aka the image name) and a tag or digest.
88
/// </summary>
9-
internal readonly record struct SourceImageReference(Registry? Registry, string Repository, string Tag)
9+
internal readonly record struct SourceImageReference(Registry? Registry, string Repository, string? Tag, string? Digest)
1010
{
1111
public override string ToString()
1212
{
13-
if (Registry is { } reg)
13+
string sourceImageReference = Repository;
14+
15+
if (Registry is { } reg)
1416
{
15-
return $"{reg.RegistryName}/{Repository}:{Tag}";
16-
}
17-
else
17+
sourceImageReference = $"{reg.RegistryName}/{sourceImageReference}";
18+
}
19+
20+
if (!string.IsNullOrEmpty(Tag))
1821
{
19-
return RepositoryAndTag;
22+
sourceImageReference = $"{sourceImageReference}:{Tag}";
2023
}
24+
25+
if (!string.IsNullOrEmpty(Digest))
26+
{
27+
sourceImageReference = $"{sourceImageReference}@{Digest}";
28+
}
29+
30+
return sourceImageReference;
2131
}
2232

2333
/// <summary>
2434
/// Returns the repository and tag as a formatted string. Used in cases
2535
/// </summary>
26-
public readonly string RepositoryAndTag => $"{Repository}:{Tag}";
36+
public string Reference
37+
=> !string.IsNullOrEmpty(Digest) ? Digest : !string.IsNullOrEmpty(Tag) ? Tag : "latest";
2738
}

src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.Interface.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,14 @@ partial class CreateNewImage
3434
/// The base image tag.
3535
/// Ex: 6.0
3636
/// </summary>
37-
[Required]
3837
public string BaseImageTag { get; set; }
3938

39+
/// <summary>
40+
/// The base image digest.
41+
/// Ex: sha256:12345...
42+
/// </summary>
43+
public string BaseImageDigest { get; set; }
44+
4045
/// <summary>
4146
/// The registry to push to.
4247
/// </summary>
@@ -187,6 +192,7 @@ public CreateNewImage()
187192
BaseRegistry = "";
188193
BaseImageName = "";
189194
BaseImageTag = "";
195+
BaseImageDigest = "";
190196
OutputRegistry = "";
191197
ArchiveOutputPath = "";
192198
Repository = "";

src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImage.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ internal async Task<bool> ExecuteAsync(CancellationToken cancellationToken)
6262

6363
RegistryMode sourceRegistryMode = BaseRegistry.Equals(OutputRegistry, StringComparison.InvariantCultureIgnoreCase) ? RegistryMode.PullFromOutput : RegistryMode.Pull;
6464
Registry? sourceRegistry = IsLocalPull ? null : new Registry(BaseRegistry, logger, sourceRegistryMode);
65-
SourceImageReference sourceImageReference = new(sourceRegistry, BaseImageName, BaseImageTag);
65+
SourceImageReference sourceImageReference = new(sourceRegistry, BaseImageName, BaseImageTag, BaseImageDigest);
6666

6767
DestinationImageReference destinationImageReference = DestinationImageReference.CreateFromSettings(
6868
Repository,
@@ -82,15 +82,15 @@ internal async Task<bool> ExecuteAsync(CancellationToken cancellationToken)
8282
var picker = new RidGraphManifestPicker(RuntimeIdentifierGraphPath);
8383
imageBuilder = await registry.GetImageManifestAsync(
8484
BaseImageName,
85-
BaseImageTag,
85+
sourceImageReference.Reference,
8686
ContainerRuntimeIdentifier,
8787
picker,
8888
cancellationToken).ConfigureAwait(false);
8989
}
9090
catch (RepositoryNotFoundException)
9191
{
9292
telemetry.LogUnknownRepository();
93-
Log.LogErrorWithCodeFromResources(nameof(Strings.RepositoryNotFound), BaseImageName, BaseImageTag, registry.RegistryName);
93+
Log.LogErrorWithCodeFromResources(nameof(Strings.RepositoryNotFound), BaseImageName, BaseImageTag, BaseImageDigest, registry.RegistryName);
9494
return !Log.HasLoggedErrors;
9595
}
9696
catch (UnableToAccessRepositoryException)

src/Containers/Microsoft.NET.Build.Containers/Tasks/CreateNewImageToolTask.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ internal string GenerateCommandLineCommandsInt()
107107
{
108108
builder.AppendSwitchIfNotNull("--baseimagetag ", BaseImageTag);
109109
}
110+
if (!string.IsNullOrWhiteSpace(BaseImageDigest))
111+
{
112+
builder.AppendSwitchIfNotNull("--baseimagedigest ", BaseImageDigest);
113+
}
110114
if (!string.IsNullOrWhiteSpace(OutputRegistry))
111115
{
112116
builder.AppendSwitchIfNotNull("--outputregistry ", OutputRegistry);

src/Containers/Microsoft.NET.Build.Containers/Tasks/ParseContainerProperties.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ public sealed class ParseContainerProperties : Microsoft.Build.Utilities.Task
4848
[Output]
4949
public string ParsedContainerTag { get; private set; }
5050

51+
[Output]
52+
public string ParsedContainerDigest { get; private set; }
53+
5154
[Output]
5255
public string NewContainerRegistry { get; private set; }
5356

@@ -71,6 +74,7 @@ public ParseContainerProperties()
7174
ParsedContainerRegistry = "";
7275
ParsedContainerImage = "";
7376
ParsedContainerTag = "";
77+
ParsedContainerDigest = "";
7478
NewContainerRegistry = "";
7579
NewContainerRepository = "";
7680
NewContainerTags = Array.Empty<string>();
@@ -132,7 +136,7 @@ public override bool Execute()
132136
out string? outputReg,
133137
out string? outputImage,
134138
out string? outputTag,
135-
out string? _outputDigest,
139+
out string? outputDigest,
136140
out bool isRegistrySpecified))
137141
{
138142
Log.LogErrorWithCodeFromResources(nameof(Strings.BaseImageNameParsingFailed), nameof(FullyQualifiedBaseImageName), FullyQualifiedBaseImageName);
@@ -163,6 +167,7 @@ public override bool Execute()
163167
ParsedContainerRegistry = outputReg ?? "";
164168
ParsedContainerImage = outputImage ?? "";
165169
ParsedContainerTag = outputTag ?? "";
170+
ParsedContainerDigest = outputDigest ?? "";
166171
NewContainerRegistry = ContainerRegistry;
167172
NewContainerTags = validTags;
168173

@@ -172,6 +177,7 @@ public override bool Execute()
172177
Log.LogMessage(MessageImportance.Low, "Host: {0}", ParsedContainerRegistry);
173178
Log.LogMessage(MessageImportance.Low, "Image: {0}", ParsedContainerImage);
174179
Log.LogMessage(MessageImportance.Low, "Tag: {0}", ParsedContainerTag);
180+
Log.LogMessage(MessageImportance.Low, "Digest: {0}", ParsedContainerDigest);
175181
Log.LogMessage(MessageImportance.Low, "Image Name: {0}", NewContainerRepository);
176182
Log.LogMessage(MessageImportance.Low, "Image Tags: {0}", string.Join(", ", NewContainerTags));
177183
}

src/Containers/containerize/ContainerizeCommand.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ internal class ContainerizeCommand : CliRootCommand
3535
DefaultValueFactory = (_) => "latest"
3636
};
3737

38+
internal CliOption<string> BaseImageDigestOption { get; } = new("--baseimagedigest")
39+
{
40+
Description = "The base image digest. Ex: sha256:6cec3641...",
41+
Required = false
42+
};
43+
3844
internal CliOption<string> OutputRegistryOption { get; } = new("--outputregistry")
3945
{
4046
Description = "The registry to push to.",
@@ -204,6 +210,7 @@ internal ContainerizeCommand() : base("Containerize an application without Docke
204210
Options.Add(BaseRegistryOption);
205211
Options.Add(BaseImageNameOption);
206212
Options.Add(BaseImageTagOption);
213+
Options.Add(BaseImageDigestOption);
207214
Options.Add(OutputRegistryOption);
208215
Options.Add(ArchiveOutputPathOption);
209216
Options.Add(RepositoryOption);
@@ -232,6 +239,7 @@ internal ContainerizeCommand() : base("Containerize an application without Docke
232239
string _baseReg = parseResult.GetValue(BaseRegistryOption)!;
233240
string _baseName = parseResult.GetValue(BaseImageNameOption)!;
234241
string _baseTag = parseResult.GetValue(BaseImageTagOption)!;
242+
string? _baseDigest = parseResult.GetValue(BaseImageDigestOption);
235243
string? _outputReg = parseResult.GetValue(OutputRegistryOption);
236244
string? _archiveOutputPath = parseResult.GetValue(ArchiveOutputPathOption);
237245
string _name = parseResult.GetValue(RepositoryOption)!;
@@ -264,6 +272,7 @@ await ContainerBuilder.ContainerizeAsync(
264272
_baseReg,
265273
_baseName,
266274
_baseTag,
275+
_baseDigest,
267276
_entrypoint,
268277
_entrypointArgs,
269278
_defaultArgs,

0 commit comments

Comments
 (0)