Browse Source

Small tweak to docker build (#498)

* Small tweak to docker build
- Log the output to both the main window and the service logs when building.

Fixes #491
pull/500/head
David Fowler 6 years ago
committed by GitHub
parent
commit
3de1aff106
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 119
      src/Microsoft.Tye.Hosting/DockerRunner.cs
  2. 2
      test/Test.Infrastructure/TestHelpers.cs

119
src/Microsoft.Tye.Hosting/DockerRunner.cs

@ -204,21 +204,6 @@ namespace Microsoft.Tye.Hosting
var hostname = "host.docker.internal";
var dockerImage = docker.Image ?? service.Description.Name;
if (docker.DockerFile != null)
{
var dockerBuildResult = await ProcessUtil.RunAsync(
$"docker",
$"build \"{docker.DockerFileContext?.FullName}\" -t {dockerImage} -f \"{docker.DockerFile}\"",
docker.WorkingDirectory,
throwOnError: false);
if (dockerBuildResult.ExitCode != 0)
{
_logger.LogInformation("Running docker command with exception info {ExceptionStdOut} {ExceptionStdErr}", dockerBuildResult.StandardOutput, dockerBuildResult.StandardError);
throw new CommandException("'docker build' failed.");
}
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
// See: https://github.com/docker/for-linux/issues/264
@ -229,9 +214,7 @@ namespace Microsoft.Tye.Hosting
hostname = addresses[0].ToString();
}
var dockerInfo = new DockerInformation(new Task[service.Description.Replicas]);
async Task RunDockerContainer(IEnumerable<(int ExternalPort, int Port, int? ContainerPort, string? Protocol)> ports)
async Task RunDockerContainer(IEnumerable<(int ExternalPort, int Port, int? ContainerPort, string? Protocol)> ports, CancellationToken cancellationToken)
{
var hasPorts = ports.Any();
@ -282,10 +265,10 @@ namespace Microsoft.Tye.Hosting
//
// The way we do proxying here doesn't really work for multi-container scenarios on linux
// without some more setup.
application.PopulateEnvironment(service, (key, value) => environment[key] = value, hostname);
application.PopulateEnvironment(service, (key, value) => environment[key] = value, hostname!);
environment["APP_INSTANCE"] = replica;
environment["CONTAINER_HOST"] = hostname;
environment["CONTAINER_HOST"] = hostname!;
status.Environment = environment;
@ -321,7 +304,7 @@ namespace Microsoft.Tye.Hosting
"docker",
command,
throwOnError: false,
cancellationToken: dockerInfo.StoppingTokenSource.Token,
cancellationToken: cancellationToken,
outputDataReceived: data => service.Logs.OnNext($"[{replica}]: {data}"),
errorDataReceived: data => service.Logs.OnNext($"[{replica}]: {data}"));
@ -355,7 +338,7 @@ namespace Microsoft.Tye.Hosting
if (!string.IsNullOrEmpty(dockerNetwork))
{
status.DockerNetworkAlias = docker.NetworkAlias ?? serviceDescription.Name;
status.DockerNetworkAlias = docker.NetworkAlias ?? serviceDescription!.Name;
var networkCommand = $"network connect {dockerNetwork} {replica} --alias {status.DockerNetworkAlias}";
@ -370,7 +353,7 @@ namespace Microsoft.Tye.Hosting
var sentStartedEvent = false;
while (!dockerInfo.StoppingTokenSource.Token.IsCancellationRequested)
while (!cancellationToken.IsCancellationRequested)
{
if (sentStartedEvent)
{
@ -396,7 +379,7 @@ namespace Microsoft.Tye.Hosting
service.ReplicaEvents.OnNext(new ReplicaEvent(ReplicaState.Started, status));
sentStartedEvent = true;
await using var _ = dockerInfo.StoppingTokenSource.Token.Register(() => status.StoppingTokenSource.Cancel());
await using var _ = cancellationToken.Register(() => status.StoppingTokenSource.Cancel());
_logger.LogInformation("Collecting docker logs for {ContainerName}.", replica);
@ -473,34 +456,79 @@ namespace Microsoft.Tye.Hosting
service.ReplicaEvents.OnNext(new ReplicaEvent(ReplicaState.Removed, status));
};
if (serviceDescription.Bindings.Count > 0)
async Task DockerBuildAsync(CancellationToken cancellationToken)
{
// Each replica is assigned a list of internal ports, one mapped to each external
// port
for (var i = 0; i < serviceDescription.Replicas; i++)
if (docker.DockerFile != null)
{
var ports = new List<(int, int, int?, string?)>();
foreach (var binding in serviceDescription.Bindings)
{
if (binding.Port == null)
{
continue;
}
_logger.LogInformation("Building docker image {Image} from docker file", dockerImage);
ports.Add((binding.Port.Value, binding.ReplicaPorts[i], binding.ContainerPort, binding.Protocol));
void Log(string data)
{
_logger.LogInformation("[" + serviceDescription!.Name + "]:" + data);
service.Logs.OnNext(data);
}
dockerInfo.Tasks[i] = RunDockerContainer(ports);
var dockerBuildResult = await ProcessUtil.RunAsync(
$"docker",
$"build \"{docker.DockerFileContext?.FullName}\" -t {dockerImage} -f \"{docker.DockerFile}\"",
outputDataReceived: Log,
errorDataReceived: Log,
workingDirectory: docker.WorkingDirectory,
cancellationToken: cancellationToken,
throwOnError: false);
if (dockerBuildResult.ExitCode != 0)
{
throw new CommandException("'docker build' failed.");
}
}
}
else
Task DockerRunAsync(CancellationToken cancellationToken)
{
for (var i = 0; i < service.Description.Replicas; i++)
var tasks = new Task[serviceDescription!.Replicas];
if (serviceDescription.Bindings.Count > 0)
{
// Each replica is assigned a list of internal ports, one mapped to each external
// port
for (var i = 0; i < serviceDescription.Replicas; i++)
{
var ports = new List<(int, int, int?, string?)>();
foreach (var binding in serviceDescription.Bindings)
{
if (binding.Port == null)
{
continue;
}
ports.Add((binding.Port.Value, binding.ReplicaPorts[i], binding.ContainerPort, binding.Protocol));
}
tasks[i] = RunDockerContainer(ports, cancellationToken);
}
}
else
{
dockerInfo.Tasks[i] = RunDockerContainer(Enumerable.Empty<(int, int, int?, string?)>());
for (var i = 0; i < service.Description.Replicas; i++)
{
tasks[i] = RunDockerContainer(Enumerable.Empty<(int, int, int?, string?)>(), cancellationToken);
}
}
return Task.WhenAll(tasks);
}
async Task BuildAndRunAsync(CancellationToken cancellationToken)
{
await DockerBuildAsync(cancellationToken);
await DockerRunAsync(cancellationToken);
}
var dockerInfo = new DockerInformation();
dockerInfo.Task = BuildAndRunAsync(dockerInfo.StoppingTokenSource.Token);
service.Items[typeof(DockerInformation)] = dockerInfo;
}
@ -511,7 +539,7 @@ namespace Microsoft.Tye.Hosting
{
var container = replica["container"];
await ProcessUtil.RunAsync("docker", $"rm -f {container}", throwOnError: false);
_logger.LogInformation("removed contaienr {container} from previous run", container);
_logger.LogInformation("removed container {container} from previous run", container);
}
_replicaRegistry.DeleteStore(DockerReplicaStore);
@ -547,7 +575,7 @@ namespace Microsoft.Tye.Hosting
{
di.StoppingTokenSource.Cancel();
return Task.WhenAll(di.Tasks);
return di.Task;
}
return Task.CompletedTask;
@ -555,12 +583,7 @@ namespace Microsoft.Tye.Hosting
private class DockerInformation
{
public DockerInformation(Task[] tasks)
{
Tasks = tasks;
}
public Task[] Tasks { get; }
public Task Task { get; set; } = default!;
public CancellationTokenSource StoppingTokenSource { get; } = new CancellationTokenSource();
}

2
test/Test.Infrastructure/TestHelpers.cs

@ -19,7 +19,7 @@ namespace Test.Infrastructure
{
public static class TestHelpers
{
private static readonly TimeSpan WaitForServicesTimeout = Debugger.IsAttached ? TimeSpan.FromMinutes(5) : TimeSpan.FromSeconds(20);
private static readonly TimeSpan WaitForServicesTimeout = Debugger.IsAttached ? TimeSpan.FromMinutes(5) : TimeSpan.FromMinutes(1);
// https://github.com/dotnet/aspnetcore/blob/5a0526dfd991419d5bce0d8ea525b50df2e37b04/src/Testing/src/TestPathUtilities.cs
// This can get into a bad pattern for having crazy paths in places. Eventually, especially if we use helix,

Loading…
Cancel
Save