Skip to content

Commit 48a07b6

Browse files
authored
Merge pull request #1 from tmds/fix_podman
DockerCli: fix docker being used when podman was detected.
2 parents 9bae331 + 11456a9 commit 48a07b6

File tree

4 files changed

+48
-28
lines changed

4 files changed

+48
-28
lines changed

src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ internal sealed class DockerCli : ILocalRegistry
2121

2222
private readonly ILogger _logger;
2323
private string? _commandPath;
24+
private string? _fullCommandPath;
2425

2526
public DockerCli(string? command, ILoggerFactory logger)
2627
{
@@ -38,13 +39,8 @@ public DockerCli(string? command, ILoggerFactory logger)
3839
public DockerCli(ILoggerFactory loggerFactory) : this(null, loggerFactory)
3940
{ }
4041

41-
private static string? FindFullPathFromPath(string? command)
42+
private static string FindFullPathFromPath(string command)
4243
{
43-
if (string.IsNullOrEmpty(command))
44-
{
45-
return command;
46-
}
47-
4844
foreach (string directory in (Environment.GetEnvironmentVariable("PATH") ?? string.Empty).Split(Path.PathSeparator))
4945
{
5046
string fullPath = Path.Combine(directory, command + FileNameSuffixes.CurrentPlatform.Exe);
@@ -57,19 +53,32 @@ public DockerCli(ILoggerFactory loggerFactory) : this(null, loggerFactory)
5753
return command;
5854
}
5955

60-
public async Task LoadAsync(BuiltImage image, ImageReference sourceReference, ImageReference destinationReference, CancellationToken cancellationToken)
56+
private async ValueTask<string> FindFullCommandPath(CancellationToken cancellationToken)
6157
{
62-
cancellationToken.ThrowIfCancellationRequested();
63-
string dockerPath = FindFullPathFromPath("docker") ?? "docker";
58+
if (_fullCommandPath != null)
59+
{
60+
return _fullCommandPath;
61+
}
6462

65-
string? commandPath = await GetCommandPathAsync(cancellationToken);
66-
if (commandPath is null)
63+
string? command = await GetCommandAsync(cancellationToken);
64+
if (command is null)
6765
{
6866
throw new NotImplementedException(Resource.FormatString(Strings.DockerProcessCreationFailed, Commands));
6967
}
7068

69+
_fullCommandPath = FindFullPathFromPath(command);
70+
71+
return _fullCommandPath;
72+
}
73+
74+
public async Task LoadAsync(BuiltImage image, ImageReference sourceReference, ImageReference destinationReference, CancellationToken cancellationToken)
75+
{
76+
cancellationToken.ThrowIfCancellationRequested();
77+
78+
string commandPath = await FindFullCommandPath(cancellationToken);
79+
7180
// call `docker load` and get it ready to receive input
72-
ProcessStartInfo loadInfo = new(dockerPath, $"load");
81+
ProcessStartInfo loadInfo = new(commandPath, $"load");
7382
loadInfo.RedirectStandardInput = true;
7483
loadInfo.RedirectStandardOutput = true;
7584
loadInfo.RedirectStandardError = true;
@@ -102,20 +111,20 @@ public async Task LoadAsync(BuiltImage image, ImageReference sourceReference, Im
102111
public async Task<bool> IsAvailableAsync(CancellationToken cancellationToken)
103112
{
104113
bool commandPathWasUnknown = this._commandPath is null; // avoid running the version command twice.
105-
string? commandPath = await GetCommandPathAsync(cancellationToken);
106-
if (commandPath is null)
114+
string? command = await GetCommandAsync(cancellationToken);
115+
if (command is null)
107116
{
108117
_logger.LogError($"Cannot find {Commands} executable.");
109118
return false;
110119
}
111120

112121
try
113122
{
114-
switch (commandPath)
123+
switch (command)
115124
{
116125
case DockerCommand:
117126
{
118-
JsonDocument config = GetConfig();
127+
JsonDocument config = GetDockerConfig();
119128

120129
if (!config.RootElement.TryGetProperty("ServerErrors", out JsonElement errorProperty))
121130
{
@@ -136,7 +145,7 @@ public async Task<bool> IsAvailableAsync(CancellationToken cancellationToken)
136145
case PodmanCommand:
137146
return commandPathWasUnknown || await TryRunVersionCommandAsync(PodmanCommand, cancellationToken);
138147
default:
139-
throw new NotImplementedException($"{commandPath} is an unknown command.");
148+
throw new NotImplementedException($"{command} is an unknown command.");
140149
}
141150
}
142151
catch (Exception ex)
@@ -152,16 +161,16 @@ public bool IsAvailable()
152161
=> IsAvailableAsync(default).GetAwaiter().GetResult();
153162

154163
public string? GetCommand()
155-
=> GetCommandPathAsync(default).GetAwaiter().GetResult();
164+
=> GetCommandAsync(default).GetAwaiter().GetResult();
156165

157166
/// <summary>
158167
/// Gets docker configuration.
159168
/// </summary>
160169
/// <param name="sync">when <see langword="true"/>, the method is executed synchronously.</param>
161170
/// <exception cref="DockerLoadException">when failed to retrieve docker configuration.</exception>
162-
internal static JsonDocument GetConfig()
171+
internal static JsonDocument GetDockerConfig()
163172
{
164-
string dockerPath = FindFullPathFromPath("docker") ?? "docker";
173+
string dockerPath = FindFullPathFromPath("docker");
165174
Process proc = new()
166175
{
167176
StartInfo = new ProcessStartInfo(dockerPath, "info --format=\"{{json .}}\"")
@@ -265,7 +274,7 @@ private static async Task WriteImageToStreamAsync(BuiltImage image, ImageReferen
265274
}
266275
}
267276

268-
private async ValueTask<string?> GetCommandPathAsync(CancellationToken cancellationToken)
277+
private async ValueTask<string?> GetCommandAsync(CancellationToken cancellationToken)
269278
{
270279
if (_commandPath != null)
271280
{
@@ -306,7 +315,7 @@ private static bool IsPodmanAlias()
306315
// or if it is a podman script in a trenchcoat.
307316
try
308317
{
309-
var dockerinfo = GetConfig().RootElement;
318+
var dockerinfo = GetDockerConfig().RootElement;
310319
// Docker's info output has a 'DockerRootDir' top-level property string that is a good marker,
311320
// while Podman has a 'host' top-level property object with a 'buildahVersion' subproperty
312321
var hasdockerProperty =

src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/CreateNewImageTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public void CreateNewImage_Baseline()
5252
task.BaseImageTag = "7.0";
5353

5454
task.OutputRegistry = "localhost:5010";
55-
task.LocalRegistry = "Docker";
55+
task.LocalRegistry = DockerAvailableFactAttribute.LocalRegistry;
5656
task.PublishDirectory = Path.Combine(newProjectDir.FullName, "bin", "Release", ToolsetInfo.CurrentTargetFramework, "linux-arm64", "publish");
5757
task.Repository = "dotnet/create-new-image-baseline";
5858
task.ImageTags = new[] { "latest" };
@@ -197,7 +197,7 @@ public void Tasks_EndToEnd_With_EnvironmentVariable_Validation()
197197
cni.ContainerEnvironmentVariables = pcp.NewContainerEnvironmentVariables;
198198
cni.ContainerRuntimeIdentifier = "linux-x64";
199199
cni.RuntimeIdentifierGraphPath = ToolsetUtils.GetRuntimeGraphFilePath();
200-
cni.LocalRegistry = global::Microsoft.NET.Build.Containers.KnownLocalRegistryTypes.Docker;
200+
cni.LocalRegistry = DockerAvailableFactAttribute.LocalRegistry;
201201

202202
Assert.True(cni.Execute(), FormatBuildMessages(errors));
203203

src/Tests/Microsoft.NET.Build.Containers.IntegrationTests/DockerSupportsArchInlineData.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class DockerSupportsArchInlineData : DataAttribute
1313
private static string[] LinuxPlatforms = GetSupportedLinuxPlatforms();
1414

1515
// another optimization - daemons don't switch types easily or quickly, so this is as good as static
16-
private static bool IsWindowsDaemon = GetIsWindowsDaemon();
16+
private static bool IsWindowsDockerDaemon = GetIsWindowsDockerDaemon();
1717

1818
private readonly string _arch;
1919
private readonly object[] _data;
@@ -41,7 +41,7 @@ private bool DaemonSupportsArch(string arch)
4141
}
4242
else
4343
{
44-
if (IsWindowsDaemon && arch.StartsWith("windows", StringComparison.OrdinalIgnoreCase))
44+
if (IsWindowsDockerDaemon && arch.StartsWith("windows", StringComparison.OrdinalIgnoreCase))
4545
{
4646
return true;
4747
}
@@ -68,11 +68,15 @@ private static string[] GetSupportedLinuxPlatforms()
6868
}
6969
}
7070

71-
private static bool GetIsWindowsDaemon()
71+
private static bool GetIsWindowsDockerDaemon()
7272
{
73+
if (ContainerCli.IsPodman)
74+
{
75+
return false;
76+
}
7377
// the config json has an OSType property that is either "linux" or "windows" -
7478
// we can't use this for linux arch detection because that isn't enough information.
75-
var config = DockerCli.GetConfig();
79+
var config = DockerCli.GetDockerConfig();
7680
if (config.RootElement.TryGetProperty("OSType", out JsonElement osTypeProperty))
7781
{
7882
return osTypeProperty.GetString() == "windows";

src/Tests/Microsoft.NET.Build.Containers.UnitTests/DockerAvailableUtils.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ namespace Microsoft.NET.Build.Containers.UnitTests;
55

66
public class DockerAvailableTheoryAttribute : TheoryAttribute
77
{
8+
public static string LocalRegistry => DockerCliStatus.LocalRegistry;
9+
810
public DockerAvailableTheoryAttribute(bool skipPodman = false)
911
{
1012
if (!DockerCliStatus.IsAvailable)
@@ -21,6 +23,8 @@ public DockerAvailableTheoryAttribute(bool skipPodman = false)
2123

2224
public class DockerAvailableFactAttribute : FactAttribute
2325
{
26+
public static string LocalRegistry => DockerCliStatus.LocalRegistry;
27+
2428
public DockerAvailableFactAttribute(bool skipPodman = false)
2529
{
2630
if (!DockerCliStatus.IsAvailable)
@@ -41,6 +45,9 @@ file static class DockerCliStatus
4145
{
4246
public static readonly bool IsAvailable;
4347
public static readonly string? Command;
48+
public static string LocalRegistry
49+
=> Command == DockerCli.PodmanCommand ? KnownLocalRegistryTypes.Podman
50+
: KnownLocalRegistryTypes.Docker;
4451

4552
static DockerCliStatus()
4653
{

0 commit comments

Comments
 (0)