Compare commits

...

48 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
Konrad Pabjan
59894790de Validate lines and columns for Annotations (#2082) 2022-08-24 16:02:51 -04:00
Ava Stancu
cba19c4d7e Release notes for 2.296.0 (#2078)
* Update releaseNote.md

* Update runnerversion
2022-08-23 10:42:40 -04:00
Nikola Jokic
01fd04464d Escaping key and quoting it to avoid key based command injection (#2062)
* escaping key and quoting it to avoid key based command injection

* extracted creation of flags to DockerUtil, with testing included
2022-08-23 10:42:29 -04:00
Tingluo Huang
1cb1779d6b Include step context name and start/finish time in step telemetry (#2069)
* Include step context name in telemetry.

* .
2022-08-22 21:26:52 -04:00
Nicholas Bergesen
42c86665a7 Display full job name and nested workflow details in log (#2049) 2022-08-22 17:20:58 -07:00
Ava Stancu
f9c2bf1dd7 Improved error logs for missing 'using' configuration in metadata file (#2052)
Co-authored-by: Octavia Stancu <avastancu@Octavias-MBP.home>
2022-08-16 17:17:42 +02:00
16 changed files with 323 additions and 28 deletions

View File

@@ -1,11 +1,9 @@
## Features
- GHES: Support connecting to GitHub Enterprise Server Actions Service on a subdomain
## Bugs ## Bugs
- Fixed a bug where GITHUB_ENV would not update correctly between composite action steps (#1794) - Avoid key based command injection via Docker command arguments (#2062)
- Fixed runner update bug caused by `update.sh|cmd` running too long (#2044)
## Misc ## Misc
- Bump Newtonsoft.Json from 11.0.2 to 13.0.1 (#2012) - Added step context name and start/finish time in step telemetry (#2069)
- Change a periodic token expiry log message level from `WARNING` to `VERBOSE` (#2021) - Improved error logs when there is a missing 'using' token configuration in the metadata file (#2052)
- Added full job name and nested workflow details in log (#2049)
## Windows x64 ## Windows x64
We recommend configuring the runner in a root folder of the Windows drive (e.g. "C:\actions-runner"). This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows. We recommend configuring the runner in a root folder of the Windows drive (e.g. "C:\actions-runner"). This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows.
@@ -32,7 +30,7 @@ curl -O -L https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>
tar xzf ./actions-runner-osx-x64-<RUNNER_VERSION>.tar.gz tar xzf ./actions-runner-osx-x64-<RUNNER_VERSION>.tar.gz
``` ```
## [Pre-release] OSX arm64 (Apple silicon) ## OSX arm64 (Apple silicon)
``` bash ``` bash
# Create a folder # Create a folder

View File

@@ -585,6 +585,8 @@ namespace GitHub.Runner.Worker
public void ProcessCommand(IExecutionContext context, string inputLine, ActionCommand command, ContainerInfo container) public void ProcessCommand(IExecutionContext context, string inputLine, ActionCommand command, ContainerInfo container)
{ {
ValidateLinesAndColumns(command, context);
command.Properties.TryGetValue(IssueCommandProperties.File, out string file); command.Properties.TryGetValue(IssueCommandProperties.File, out string file);
command.Properties.TryGetValue(IssueCommandProperties.Line, out string line); command.Properties.TryGetValue(IssueCommandProperties.Line, out string line);
command.Properties.TryGetValue(IssueCommandProperties.Column, out string column); command.Properties.TryGetValue(IssueCommandProperties.Column, out string column);

View File

@@ -503,7 +503,7 @@ namespace GitHub.Runner.Worker
}; };
} }
throw new NotSupportedException(nameof(ConvertRuns)); throw new NotSupportedException("Missing 'using' value. 'using' requires 'composite', 'docker', 'node12' or 'node16'.");
} }
private void ConvertInputs( private void ConvertInputs(

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

@@ -131,11 +131,11 @@ namespace GitHub.Runner.Worker.Container
{ {
if (String.IsNullOrEmpty(env.Value)) if (String.IsNullOrEmpty(env.Value))
{ {
dockerOptions.Add($"-e \"{env.Key}\""); dockerOptions.Add(DockerUtil.CreateEscapedOption("-e", env.Key));
} }
else else
{ {
dockerOptions.Add($"-e \"{env.Key}={env.Value.Replace("\"", "\\\"")}\""); dockerOptions.Add(DockerUtil.CreateEscapedOption("-e", env.Key, env.Value));
} }
} }
@@ -202,7 +202,7 @@ namespace GitHub.Runner.Worker.Container
{ {
// e.g. -e MY_SECRET maps the value into the exec'ed process without exposing // e.g. -e MY_SECRET maps the value into the exec'ed process without exposing
// the value directly in the command // the value directly in the command
dockerOptions.Add($"-e {env.Key}"); dockerOptions.Add(DockerUtil.CreateEscapedOption("-e", env.Key));
} }
// Watermark for GitHub Action environment // Watermark for GitHub Action environment

View File

@@ -17,7 +17,7 @@ namespace GitHub.Runner.Worker.Container
string pattern = $"^(?<{targetPort}>\\d+)/(?<{proto}>\\w+) -> (?<{host}>.+):(?<{hostPort}>\\d+)$"; string pattern = $"^(?<{targetPort}>\\d+)/(?<{proto}>\\w+) -> (?<{host}>.+):(?<{hostPort}>\\d+)$";
List<PortMapping> portMappings = new List<PortMapping>(); List<PortMapping> portMappings = new List<PortMapping>();
foreach(var line in portMappingLines) foreach (var line in portMappingLines)
{ {
Match m = Regex.Match(line, pattern, RegexOptions.None, TimeSpan.FromSeconds(1)); Match m = Regex.Match(line, pattern, RegexOptions.None, TimeSpan.FromSeconds(1));
if (m.Success) if (m.Success)
@@ -61,5 +61,28 @@ namespace GitHub.Runner.Worker.Container
} }
return ""; return "";
} }
public static string CreateEscapedOption(string flag, string key)
{
if (String.IsNullOrEmpty(key))
{
return "";
}
return $"{flag} \"{EscapeString(key)}\"";
}
public static string CreateEscapedOption(string flag, string key, string value)
{
if (String.IsNullOrEmpty(key))
{
return "";
}
return $"{flag} \"{EscapeString(key)}={EscapeString(value)}\"";
}
private static string EscapeString(string value)
{
return value.Replace("\\", "\\\\").Replace("\"", "\\\"");
}
} }
} }

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

@@ -369,6 +369,7 @@ namespace GitHub.Runner.Worker
child.StepTelemetry.StepId = recordId; child.StepTelemetry.StepId = recordId;
child.StepTelemetry.Stage = stage.ToString(); child.StepTelemetry.Stage = stage.ToString();
child.StepTelemetry.IsEmbedded = isEmbedded; child.StepTelemetry.IsEmbedded = isEmbedded;
child.StepTelemetry.StepContextName = child.GetFullyQualifiedContextName(); ;
return child; return child;
} }
@@ -959,6 +960,8 @@ namespace GitHub.Runner.Worker
_record.StartTime != null) _record.StartTime != null)
{ {
StepTelemetry.ExecutionTimeInSeconds = (int)Math.Ceiling((_record.FinishTime - _record.StartTime)?.TotalSeconds ?? 0); StepTelemetry.ExecutionTimeInSeconds = (int)Math.Ceiling((_record.FinishTime - _record.StartTime)?.TotalSeconds ?? 0);
StepTelemetry.StartTime = _record.StartTime;
StepTelemetry.FinishTime = _record.FinishTime;
} }
if (!IsEmbedded && if (!IsEmbedded &&

View File

@@ -193,7 +193,7 @@ namespace GitHub.Runner.Worker.Handlers
TranslateToContainerPath(environment); TranslateToContainerPath(environment);
await containerHookManager.RunScriptStepAsync(context, await containerHookManager.RunScriptStepAsync(context,
Container, Container,
workingDirectory, workingDirectory,
fileName, fileName,
arguments, arguments,
environment, environment,
@@ -216,7 +216,7 @@ namespace GitHub.Runner.Worker.Handlers
{ {
// e.g. -e MY_SECRET maps the value into the exec'ed process without exposing // e.g. -e MY_SECRET maps the value into the exec'ed process without exposing
// the value directly in the command // the value directly in the command
dockerCommandArgs.Add($"-e {env.Key}"); dockerCommandArgs.Add(DockerUtil.CreateEscapedOption("-e", env.Key));
} }
if (!string.IsNullOrEmpty(PrependPath)) if (!string.IsNullOrEmpty(PrependPath))
{ {

View File

@@ -316,6 +316,29 @@ namespace GitHub.Runner.Worker
} }
} }
if (message.Variables.TryGetValue("system.workflowFileFullPath", out VariableValue workflowFileFullPath))
{
context.Output($"Uses: {workflowFileFullPath.Value}");
if (message.ContextData.TryGetValue("inputs", out var pipelineContextData))
{
var inputs = pipelineContextData.AssertDictionary("inputs");
if (inputs.Any())
{
context.Output($"##[group] Inputs");
foreach (var input in inputs)
{
context.Output($" {input.Key}: {input.Value}");
}
context.Output("##[endgroup]");
}
}
if (!string.IsNullOrWhiteSpace(message.JobDisplayName))
{
context.Output($"Complete job name: {message.JobDisplayName}");
}
}
var intraActionStates = new Dictionary<Guid, Dictionary<string, string>>(); var intraActionStates = new Dictionary<Guid, Dictionary<string, string>>();
foreach (var preStep in prepareResult.PreStepTracker) foreach (var preStep in prepareResult.PreStepTracker)
{ {

View File

@@ -30,6 +30,9 @@ namespace GitHub.DistributedTask.WebApi
[DataMember(EmitDefaultValue = false)] [DataMember(EmitDefaultValue = false)]
public Guid StepId { get; set; } public Guid StepId { get; set; }
[DataMember(EmitDefaultValue = false)]
public string StepContextName { get; set; }
[DataMember(EmitDefaultValue = false)] [DataMember(EmitDefaultValue = false)]
public bool? HasRunsStep { get; set; } public bool? HasRunsStep { get; set; }
@@ -57,6 +60,12 @@ namespace GitHub.DistributedTask.WebApi
[DataMember(EmitDefaultValue = false)] [DataMember(EmitDefaultValue = false)]
public int? ExecutionTimeInSeconds { get; set; } public int? ExecutionTimeInSeconds { get; set; }
[DataMember(EmitDefaultValue = false)]
public DateTime? StartTime { get; set; }
[DataMember(EmitDefaultValue = false)]
public DateTime? FinishTime { get; set; }
[DataMember(EmitDefaultValue = false)] [DataMember(EmitDefaultValue = false)]
public string ContainerHookData { get; set; } public string ContainerHookData { get; set; }
} }

View File

@@ -144,5 +144,59 @@ namespace GitHub.Runner.Common.Tests.Worker.Container
var actual = DockerUtil.ParseRegistryHostnameFromImageName(input); var actual = DockerUtil.ParseRegistryHostnameFromImageName(input);
Assert.Equal(expected, actual); Assert.Equal(expected, actual);
} }
[Theory]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
[InlineData("", "")]
[InlineData("HOME alpine:3.8 sh -c id #", "HOME alpine:3.8 sh -c id #")]
[InlineData("HOME \"alpine:3.8 sh -c id #", "HOME \\\"alpine:3.8 sh -c id #")]
[InlineData("HOME \\\"alpine:3.8 sh -c id #", "HOME \\\\\\\"alpine:3.8 sh -c id #")]
[InlineData("HOME \\\\\"alpine:3.8 sh -c id #", "HOME \\\\\\\\\\\"alpine:3.8 sh -c id #")]
[InlineData("HOME \"\"alpine:3.8 sh -c id #", "HOME \\\"\\\"alpine:3.8 sh -c id #")]
[InlineData("HOME \\\"\"alpine:3.8 sh -c id #", "HOME \\\\\\\"\\\"alpine:3.8 sh -c id #")]
[InlineData("HOME \"\\\"alpine:3.8 sh -c id #", "HOME \\\"\\\\\\\"alpine:3.8 sh -c id #")]
public void CreateEscapedOption_keyOnly(string input, string escaped)
{
var flag = "--example";
var actual = DockerUtil.CreateEscapedOption(flag, input);
string expected;
if (String.IsNullOrEmpty(input))
{
expected = "";
}
else
{
expected = $"{flag} \"{escaped}\"";
}
Assert.Equal(expected, actual);
}
[Theory]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
[InlineData("HOME", "", "HOME", "")]
[InlineData("HOME alpine:3.8 sh -c id #", "HOME alpine:3.8 sh -c id #", "HOME alpine:3.8 sh -c id #", "HOME alpine:3.8 sh -c id #")]
[InlineData("HOME \"alpine:3.8 sh -c id #", "HOME \"alpine:3.8 sh -c id #", "HOME \\\"alpine:3.8 sh -c id #", "HOME \\\"alpine:3.8 sh -c id #")]
[InlineData("HOME \\\"alpine:3.8 sh -c id #", "HOME \\\"alpine:3.8 sh -c id #", "HOME \\\\\\\"alpine:3.8 sh -c id #", "HOME \\\\\\\"alpine:3.8 sh -c id #")]
[InlineData("HOME \\\\\"alpine:3.8 sh -c id #", "HOME \\\\\"alpine:3.8 sh -c id #", "HOME \\\\\\\\\\\"alpine:3.8 sh -c id #", "HOME \\\\\\\\\\\"alpine:3.8 sh -c id #")]
[InlineData("HOME \"\"alpine:3.8 sh -c id #", "HOME \"\"alpine:3.8 sh -c id #", "HOME \\\"\\\"alpine:3.8 sh -c id #", "HOME \\\"\\\"alpine:3.8 sh -c id #")]
[InlineData("HOME \\\"\"alpine:3.8 sh -c id #", "HOME \\\"\"alpine:3.8 sh -c id #", "HOME \\\\\\\"\\\"alpine:3.8 sh -c id #", "HOME \\\\\\\"\\\"alpine:3.8 sh -c id #")]
[InlineData("HOME \"\\\"alpine:3.8 sh -c id #", "HOME \"\\\"alpine:3.8 sh -c id #", "HOME \\\"\\\\\\\"alpine:3.8 sh -c id #", "HOME \\\"\\\\\\\"alpine:3.8 sh -c id #")]
public void CreateEscapedOption_keyValue(string keyInput, string valueInput, string escapedKey, string escapedValue)
{
var flag = "--example";
var actual = DockerUtil.CreateEscapedOption(flag, keyInput, valueInput);
string expected;
if (String.IsNullOrEmpty(keyInput))
{
expected = "";
}
else
{
expected = $"{flag} \"{escapedKey}={escapedValue}\"";
}
Assert.Equal(expected, actual);
}
} }
} }

View File

@@ -698,6 +698,31 @@ namespace GitHub.Runner.Common.Tests.Worker
} }
} }
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Load_CompositeActionNoUsing()
{
try
{
//Arrange
Setup();
var actionManifest = new ActionManifestManager();
actionManifest.Initialize(_hc);
var action_path = Path.Combine(TestUtil.GetTestDataPath(), "composite_action_without_using_token.yml");
//Assert
var err = Assert.Throws<ArgumentException>(() => actionManifest.Load(_ec.Object, action_path));
Assert.Contains($"Fail to load {action_path}", err.Message);
_ec.Verify(x => x.AddIssue(It.Is<Issue>(s => s.Message.Contains("Missing 'using' value. 'using' requires 'composite', 'docker', 'node12' or 'node16'.")), It.IsAny<string>()), Times.Once);
}
finally
{
Teardown();
}
}
[Fact] [Fact]
[Trait("Level", "L0")] [Trait("Level", "L0")]
[Trait("Category", "Worker")] [Trait("Category", "Worker")]

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);
}
}
}

View File

@@ -0,0 +1,9 @@
name: "composite action"
description: "test composite action without value for the 'using' token in 'runs'"
runs:
steps:
- id: mystep
shell: bash
run: |
echo "hello world"

View File

@@ -1 +1 @@
2.295.0 2.296.0