Compare commits

...

42 Commits

Author SHA1 Message Date
JoannaaKL
4f8c069bf3 Do not fail service containers without the healthcheck 2022-10-05 08:39:58 +00:00
JoannaaKL
0d782f477f Revert "Check service exit code if there is no healtcheck configured"
This reverts commit fec24e8341.
2022-10-03 13:50:35 +00:00
JoannaaKL
f39a18dfd2 Remove unnecessary healthcheck for healthy service container 2022-09-27 07:59:44 +00:00
JoannaaKL
fec24e8341 Check service exit code if there is no healtcheck configured 2022-09-16 11:09:55 +00:00
JoannaaKL
1778f8f022 Add back test asserting exception 2022-09-16 07:53:12 +00:00
JoannaaKL
c5685b7c63 Update src/Test/L0/Worker/ContainerOperationProviderL0.cs
Co-authored-by: Tingluo Huang <tingluohuang@github.com>
2022-09-16 09:23:44 +02:00
JoannaaKL
82b81f26ed Update src/Test/L0/Worker/ContainerOperationProviderL0.cs
Co-authored-by: Tingluo Huang <tingluohuang@github.com>
2022-09-16 09:23:32 +02:00
JoannaaKL
b1dd7975bc Update src/Test/L0/Worker/ContainerOperationProviderL0.cs
Co-authored-by: Tingluo Huang <tingluohuang@github.com>
2022-09-16 09:23:17 +02:00
JoannaaKL
75c40e3cac Update src/Test/L0/Worker/ContainerOperationProviderL0.cs
Co-authored-by: Tingluo Huang <tingluohuang@github.com>
2022-09-16 09:22:46 +02:00
JoannaaKL
d3f463b4b0 Update src/Test/L0/Worker/ContainerOperationProviderL0.cs
Co-authored-by: Tingluo Huang <tingluohuang@github.com>
2022-09-16 09:22:06 +02:00
JoannaaKL
3b30e5c0d5 Add configure await 2022-09-15 19:24:57 +00:00
JoannaaKL
805d6fdb39 remove test asserting thrown exception 2022-09-15 19:17:22 +00:00
JoannaaKL
7ba0916092 Unextract the container error logs method 2022-09-15 19:04:13 +00:00
JoannaaKL
72fe5798c3 Make test sequential 2022-09-15 18:55:37 +00:00
JoannaaKL
b15525a8ca Rename Healthcheck back to ContainerHealthcheck 2022-09-15 07:58:33 +00:00
JoannaaKL
a9fa7f83e9 Update src/Runner.Worker/ContainerOperationProvider.cs
Co-authored-by: Tingluo Huang <tingluohuang@github.com>
2022-09-15 09:31:37 +02:00
JoannaaKL
5b26a1e1ae Update src/Runner.Worker/ContainerOperationProvider.cs
Co-authored-by: Tingluo Huang <tingluohuang@github.com>
2022-09-15 09:31:09 +02:00
JoannaaKL
e03ad87776 Remove the default value for bool variable 2022-09-14 08:40:51 +00:00
JoannaaKL
826cec2775 Refactor healthcheck logic to separate method to enable unit testing. 2022-09-14 08:34:27 +00:00
JoannaaKL
b8aafc4ff1 Rename boolean flag indicating service container failure 2022-09-13 13:51:52 +00:00
JoannaaKL
47ead99c13 Remove unnecessary field 'UnhealthyContainers' 2022-09-13 13:47:58 +00:00
JoannaaKL
3c88e14ca5 Add newline to TestHostContext 2022-09-13 12:48:31 +00:00
JoannaaKL
f5461b78be Remove printHello() function 2022-09-13 12:45:35 +00:00
JoannaaKL
912d7d6932 Remove unnecessary 'IsAnyUnhealthy' flag 2022-09-13 12:44:22 +00:00
JoannaaKL
76e4f51a21 Removed the test testing the old logic flow. 2022-09-09 15:21:18 +00:00
AStancu
7a992f844d Moved service containers error logs to separate group sections 2022-09-09 14:31:20 +00:00
JoannaaKL
dfcbe9b1e1 Added back section group. 2022-09-08 15:59:06 +00:00
JoannaaKL
2dc8f25359 Removed unused import 2022-09-08 15:52:13 +00:00
JoannaaKL
f0b315c911 Change the logic for printing Service Containers logs
Service container logs will be printed in the 'Start containers' section only if there is an error.
Healthy services will have their logs printed in the 'Stop Containers' section.
2022-09-08 15:50:22 +00:00
JoannaaKL
354c8bcbed Update src/Test/L0/TestHostContext.cs
Co-authored-by: Tingluo Huang <tingluohuang@github.com>
2022-09-07 17:19:21 +02:00
JoannaaKL
ad8f17e956 Update src/Runner.Worker/Container/DockerCommandManager.cs
Co-authored-by: Tingluo Huang <tingluohuang@github.com>
2022-09-07 17:19:04 +02:00
JoannaaKL
2d4dc37d49 Removing the section 'Waiting for all services to be ready'
Since nested subsections are not being displayed properly and we already need one subsection per service error.
2022-09-07 08:39:57 +00:00
AStancu
cfcf0831f3 Added execution context error
This will make a failed health check more visible in the UI without disrupting the execution of the program.
2022-09-07 00:10:18 +02:00
Ava S
94e504c40e Removed recently added method to inspect docker logs
The method was doing the same thing as the existing DockerLogs method.
2022-09-07 00:07:13 +02:00
AStancu
2f9ced96c6 Print service containers only if they were healthy
Unhealthy service logs are printed in ContainerHealthCheckLogs called prior to this step.
2022-09-06 19:17:48 +00:00
Ava S
29e26c0aa1 Updated the container logs sub-section message 2022-09-06 17:21:12 +02:00
JoannaaKL
d533271015 Removed duplicated logging to the executionContext 2022-09-06 09:51:30 +00:00
JoannaaKL
892a90cc99 Removed the exception thrown if the service container was not healthy 2022-09-06 09:22:12 +00:00
Ava S
ae7bb31431 placed the docker logs output in dedicated ##group section 2022-09-06 10:10:52 +02:00
JoannaaKL
160d07e576 Adding another test to ContainerOperationProvider 2022-08-31 13:55:18 +00:00
JoannaaKL
2e8d8a74ab Adding Unit test to ContainerOperationProvider 2022-08-31 10:05:44 +00:00
Ava S
3b4406161b adding support for a service container docker logs 2022-08-30 12:24:03 +02:00
3 changed files with 162 additions and 13 deletions

View File

@@ -92,6 +92,8 @@ namespace GitHub.Runner.Worker.Container
public bool IsJobContainer { get; set; } public bool IsJobContainer { get; set; }
public bool IsAlpine { get; set; } public bool IsAlpine { get; set; }
public bool FailedInitialization { get; set; }
public IDictionary<string, string> ContainerEnvironmentVariables public IDictionary<string, string> ContainerEnvironmentVariables
{ {
get get

View File

@@ -98,12 +98,41 @@ namespace GitHub.Runner.Worker
await StartContainerAsync(executionContext, container); await StartContainerAsync(executionContext, container);
} }
await RunContainersHealthcheck(executionContext, containers);
}
public async Task RunContainersHealthcheck(IExecutionContext executionContext, List<ContainerInfo> containers)
{
executionContext.Output("##[group]Waiting for all services to be ready"); executionContext.Output("##[group]Waiting for all services to be ready");
var unhealthyContainers = new List<ContainerInfo>();
foreach (var container in containers.Where(c => !c.IsJobContainer)) foreach (var container in containers.Where(c => !c.IsJobContainer))
{ {
await ContainerHealthcheck(executionContext, container); var healthcheck = await ContainerHealthcheck(executionContext, container);
if (!(string.Equals(healthcheck, "healthy", StringComparison.OrdinalIgnoreCase) || string.IsNullOrEmpty(healthcheck)))
{
unhealthyContainers.Add(container);
}
else
{
executionContext.Output($"{container.ContainerNetworkAlias} service is healthy.");
}
} }
executionContext.Output("##[endgroup]"); executionContext.Output("##[endgroup]");
if (unhealthyContainers.Count > 0)
{
foreach (var container in unhealthyContainers)
{
executionContext.Output($"##[group]Service container {container.ContainerNetworkAlias} failed.");
await _dockerManager.DockerLogs(context: executionContext, containerId: container.ContainerId);
executionContext.Error($"Failed to initialize container {container.ContainerImage}");
container.FailedInitialization = true;
executionContext.Output("##[endgroup]");
}
throw new InvalidOperationException("One or more containers failed to start.");
}
} }
public async Task StopContainersAsync(IExecutionContext executionContext, object data) public async Task StopContainersAsync(IExecutionContext executionContext, object data)
@@ -299,9 +328,8 @@ namespace GitHub.Runner.Worker
if (!string.IsNullOrEmpty(container.ContainerId)) if (!string.IsNullOrEmpty(container.ContainerId))
{ {
if (!container.IsJobContainer) if (!container.IsJobContainer && !container.FailedInitialization)
{ {
// Print logs for service container jobs (not the "action" job itself b/c that's already logged).
executionContext.Output($"Print service container logs: {container.ContainerDisplayName}"); executionContext.Output($"Print service container logs: {container.ContainerDisplayName}");
int logsExitCode = await _dockerManager.DockerLogs(executionContext, container.ContainerId); int logsExitCode = await _dockerManager.DockerLogs(executionContext, container.ContainerId);
@@ -395,14 +423,14 @@ namespace GitHub.Runner.Worker
} }
} }
private async Task ContainerHealthcheck(IExecutionContext executionContext, ContainerInfo container) private async Task<string> ContainerHealthcheck(IExecutionContext executionContext, ContainerInfo container)
{ {
string healthCheck = "--format=\"{{if .Config.Healthcheck}}{{print .State.Health.Status}}{{end}}\""; string healthCheck = "--format=\"{{if .Config.Healthcheck}}{{print .State.Health.Status}}{{end}}\"";
string serviceHealth = (await _dockerManager.DockerInspect(context: executionContext, dockerObject: container.ContainerId, options: healthCheck)).FirstOrDefault(); string serviceHealth = (await _dockerManager.DockerInspect(context: executionContext, dockerObject: container.ContainerId, options: healthCheck)).FirstOrDefault();
if (string.IsNullOrEmpty(serviceHealth)) if (string.IsNullOrEmpty(serviceHealth))
{ {
// Container has no HEALTHCHECK // Container has no HEALTHCHECK
return; return String.Empty;
} }
var retryCount = 0; var retryCount = 0;
while (string.Equals(serviceHealth, "starting", StringComparison.OrdinalIgnoreCase)) while (string.Equals(serviceHealth, "starting", StringComparison.OrdinalIgnoreCase))
@@ -413,14 +441,7 @@ namespace GitHub.Runner.Worker
serviceHealth = (await _dockerManager.DockerInspect(context: executionContext, dockerObject: container.ContainerId, options: healthCheck)).FirstOrDefault(); serviceHealth = (await _dockerManager.DockerInspect(context: executionContext, dockerObject: container.ContainerId, options: healthCheck)).FirstOrDefault();
retryCount++; retryCount++;
} }
if (string.Equals(serviceHealth, "healthy", StringComparison.OrdinalIgnoreCase)) return serviceHealth;
{
executionContext.Output($"{container.ContainerNetworkAlias} service is healthy.");
}
else
{
throw new InvalidOperationException($"Failed to initialize, {container.ContainerNetworkAlias} service is {serviceHealth}.");
}
} }
private async Task<string> ContainerRegistryLogin(IExecutionContext executionContext, ContainerInfo container) private async Task<string> ContainerRegistryLogin(IExecutionContext executionContext, ContainerInfo container)

View File

@@ -0,0 +1,126 @@
using GitHub.Runner.Worker;
using GitHub.Runner.Worker.Container;
using Xunit;
using Moq;
using GitHub.Runner.Worker.Container.ContainerHooks;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using GitHub.DistributedTask.WebApi;
using System;
namespace GitHub.Runner.Common.Tests.Worker
{
public sealed class ContainerOperationProviderL0
{
private TestHostContext _hc;
private Mock<IExecutionContext> _ec;
private Mock<IDockerCommandManager> _dockerManager;
private Mock<IContainerHookManager> _containerHookManager;
private ContainerOperationProvider containerOperationProvider;
private Mock<IJobServerQueue> serverQueue;
private Mock<IPagingLogger> pagingLogger;
private List<string> healthyDockerStatus = new List<string> { "healthy" };
private List<string> emptyDockerStatus = new List<string> { string.Empty };
private List<string> unhealthyDockerStatus = new List<string> { "unhealthy" };
private List<string> dockerLogs = new List<string> { "log1", "log2", "log3" };
List<ContainerInfo> containers = new List<ContainerInfo>();
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async void RunServiceContainersHealthcheck_UnhealthyServiceContainer_AssertFailedTask()
{
//Arrange
Setup();
_dockerManager.Setup(x => x.DockerInspect(_ec.Object, It.IsAny<string>(), It.IsAny<string>())).Returns(Task.FromResult(unhealthyDockerStatus));
//Act
try
{
await containerOperationProvider.RunContainersHealthcheck(_ec.Object, containers);
}
catch (InvalidOperationException)
{
//Assert
Assert.Equal(TaskResult.Failed, _ec.Object.Result ?? TaskResult.Failed);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async void RunServiceContainersHealthcheck_UnhealthyServiceContainer_AssertExceptionThrown()
{
//Arrange
Setup();
_dockerManager.Setup(x => x.DockerInspect(_ec.Object, It.IsAny<string>(), It.IsAny<string>())).Returns(Task.FromResult(unhealthyDockerStatus));
//Act and Assert
await Assert.ThrowsAsync<InvalidOperationException>(() => containerOperationProvider.RunContainersHealthcheck(_ec.Object, containers));
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async void RunServiceContainersHealthcheck_healthyServiceContainer_AssertSucceededTask()
{
//Arrange
Setup();
_dockerManager.Setup(x => x.DockerInspect(_ec.Object, It.IsAny<string>(), It.IsAny<string>())).Returns(Task.FromResult(healthyDockerStatus));
//Act
await containerOperationProvider.RunContainersHealthcheck(_ec.Object, containers);
//Assert
Assert.Equal(TaskResult.Succeeded, _ec.Object.Result ?? TaskResult.Succeeded);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async void RunServiceContainersHealthcheck_healthyServiceContainerWithoutHealthcheck_AssertSucceededTask()
{
//Arrange
Setup();
_dockerManager.Setup(x => x.DockerInspect(_ec.Object, It.IsAny<string>(), It.IsAny<string>())).Returns(Task.FromResult(emptyDockerStatus));
//Act
await containerOperationProvider.RunContainersHealthcheck(_ec.Object, containers);
//Assert
Assert.Equal(TaskResult.Succeeded, _ec.Object.Result ?? TaskResult.Succeeded);
}
private void Setup([CallerMemberName] string testName = "")
{
containers.Add(new ContainerInfo() { ContainerImage = "ubuntu:16.04" });
_hc = new TestHostContext(this, testName);
_ec = new Mock<IExecutionContext>();
serverQueue = new Mock<IJobServerQueue>();
pagingLogger = new Mock<IPagingLogger>();
_dockerManager = new Mock<IDockerCommandManager>();
_containerHookManager = new Mock<IContainerHookManager>();
containerOperationProvider = new ContainerOperationProvider();
_hc.SetSingleton<IDockerCommandManager>(_dockerManager.Object);
_hc.SetSingleton<IJobServerQueue>(serverQueue.Object);
_hc.SetSingleton<IPagingLogger>(pagingLogger.Object);
_hc.SetSingleton<IDockerCommandManager>(_dockerManager.Object);
_hc.SetSingleton<IContainerHookManager>(_containerHookManager.Object);
_ec.Setup(x => x.Global).Returns(new GlobalContext());
containerOperationProvider.Initialize(_hc);
}
}
}