public void RegexParsesPathFromDockerConfigEnv() { // Arrange var configOutput0 = new List <string> { "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "MY_VAR=test" }; var configOutput1 = new List <string> { "PATH=/bad idea:/really,bad,idea:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "MY_VAR=test" }; var configOutput2 = new List <string>(); var configOutput3 = new List <string> { "NOT_A_PATH=/bad idea:/really,bad,idea:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "MY_VAR=test" }; var configOutput4 = new List <string> { "PATH", "PATH=" }; var configOutput5 = new List <string> { "PATH=/foo/bar:/baz", "Path=/no/where" }; // Act var result0 = DockerUtil.ParsePathFromConfigEnv(configOutput0); var result1 = DockerUtil.ParsePathFromConfigEnv(configOutput1); var result2 = DockerUtil.ParsePathFromConfigEnv(configOutput2); var result3 = DockerUtil.ParsePathFromConfigEnv(configOutput3); var result4 = DockerUtil.ParsePathFromConfigEnv(configOutput4); var result5 = DockerUtil.ParsePathFromConfigEnv(configOutput5); // Assert Assert.NotNull(result0); Assert.Equal("/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", result0); Assert.NotNull(result1); Assert.Equal("/bad idea:/really,bad,idea:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", result1); Assert.NotNull(result2); Assert.Equal("", result2); Assert.NotNull(result3); Assert.Equal("", result3); Assert.NotNull(result4); Assert.Equal("", result4); Assert.NotNull(result5); Assert.Equal("/foo/bar:/baz", result5); }
private async Task StartContainerAsync(IExecutionContext executionContext, ContainerInfo container) { Trace.Entering(); ArgUtil.NotNull(executionContext, nameof(executionContext)); ArgUtil.NotNull(container, nameof(container)); ArgUtil.NotNullOrEmpty(container.ContainerImage, nameof(container.ContainerImage)); Trace.Info($"Container name: {container.ContainerName}"); Trace.Info($"Container image: {container.ContainerImage}"); Trace.Info($"Container options: {container.ContainerCreateOptions}"); var groupName = container.IsJobContainer ? "Starting job container" : $"Starting {container.ContainerNetworkAlias} service container"; executionContext.Output($"##[group]{groupName}"); foreach (var port in container.UserPortMappings) { Trace.Info($"User provided port: {port.Value}"); } foreach (var volume in container.UserMountVolumes) { Trace.Info($"User provided volume: {volume.Value}"); var mount = new MountVolume(volume.Value); if (string.Equals(mount.SourceVolumePath, "/", StringComparison.OrdinalIgnoreCase)) { executionContext.Warning($"Volume mount {volume.Value} is going to mount '/' into the container which may cause file ownership change in the entire file system and cause Actions Runner to lose permission to access the disk."); } } // Pull down docker image with retry up to 3 times int retryCount = 0; int pullExitCode = 0; while (retryCount < 3) { pullExitCode = await _dockerManger.DockerPull(executionContext, container.ContainerImage); if (pullExitCode == 0) { break; } else { retryCount++; if (retryCount < 3) { var backOff = BackoffTimerHelper.GetRandomBackoff(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(10)); executionContext.Warning($"Docker pull failed with exit code {pullExitCode}, back off {backOff.TotalSeconds} seconds before retry."); await Task.Delay(backOff); } } } if (retryCount == 3 && pullExitCode != 0) { throw new InvalidOperationException($"Docker pull failed with exit code {pullExitCode}"); } if (container.IsJobContainer) { // Configure job container - Mount workspace and tools, set up environment, and start long running process var githubContext = executionContext.ExpressionValues["github"] as GitHubContext; ArgUtil.NotNull(githubContext, nameof(githubContext)); var workingDirectory = githubContext["workspace"] as StringContextData; ArgUtil.NotNullOrEmpty(workingDirectory, nameof(workingDirectory)); container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Work), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Work)))); #if OS_WINDOWS container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Externals), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals)))); #else if (Environment.GetEnvironmentVariable("K8S_POD_NAME") != null) { container.MountVolumes.Add(new MountVolume(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), "__externals_copy"), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals)), true)); } else { container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Externals), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals)), true)); } #endif container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Temp), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Temp)))); container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Actions), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Actions)))); container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Tools), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Tools)))); var tempHomeDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Temp), "_github_home"); Directory.CreateDirectory(tempHomeDirectory); container.MountVolumes.Add(new MountVolume(tempHomeDirectory, "/github/home")); container.AddPathTranslateMapping(tempHomeDirectory, "/github/home"); container.ContainerEnvironmentVariables["HOME"] = container.TranslateToContainerPath(tempHomeDirectory); var tempWorkflowDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Temp), "_github_workflow"); Directory.CreateDirectory(tempWorkflowDirectory); container.MountVolumes.Add(new MountVolume(tempWorkflowDirectory, "/github/workflow")); container.AddPathTranslateMapping(tempWorkflowDirectory, "/github/workflow"); container.ContainerWorkDirectory = container.TranslateToContainerPath(workingDirectory); container.ContainerEntryPoint = "tail"; container.ContainerEntryPointArgs = "\"-f\" \"/dev/null\""; } container.ContainerId = await _dockerManger.DockerCreate(executionContext, container); ArgUtil.NotNullOrEmpty(container.ContainerId, nameof(container.ContainerId)); // Start container int startExitCode = await _dockerManger.DockerStart(executionContext, container.ContainerId); if (startExitCode != 0) { throw new InvalidOperationException($"Docker start fail with exit code {startExitCode}"); } try { // Make sure container is up and running var psOutputs = await _dockerManger.DockerPS(executionContext, $"--all --filter id={container.ContainerId} --filter status=running --no-trunc --format \"{{{{.ID}}}} {{{{.Status}}}}\""); if (psOutputs.FirstOrDefault(x => !string.IsNullOrEmpty(x))?.StartsWith(container.ContainerId) != true) { // container is not up and running, pull docker log for this container. await _dockerManger.DockerPS(executionContext, $"--all --filter id={container.ContainerId} --no-trunc --format \"{{{{.ID}}}} {{{{.Status}}}}\""); int logsExitCode = await _dockerManger.DockerLogs(executionContext, container.ContainerId); if (logsExitCode != 0) { executionContext.Warning($"Docker logs fail with exit code {logsExitCode}"); } executionContext.Warning($"Docker container {container.ContainerId} is not in running state."); } } catch (Exception ex) { // pull container log is best effort. Trace.Error("Catch exception when check container log and container status."); Trace.Error(ex); } // Gather runtime container information if (!container.IsJobContainer) { var service = new DictionaryContextData() { ["id"] = new StringContextData(container.ContainerId), ["ports"] = new DictionaryContextData(), ["network"] = new StringContextData(container.ContainerNetwork) }; container.AddPortMappings(await _dockerManger.DockerPort(executionContext, container.ContainerId)); foreach (var port in container.PortMappings) { (service["ports"] as DictionaryContextData)[port.ContainerPort] = new StringContextData(port.HostPort); } executionContext.JobContext.Services[container.ContainerNetworkAlias] = service; } else { var configEnvFormat = "--format \"{{range .Config.Env}}{{println .}}{{end}}\""; var containerEnv = await _dockerManger.DockerInspect(executionContext, container.ContainerId, configEnvFormat); container.ContainerRuntimePath = DockerUtil.ParsePathFromConfigEnv(containerEnv); executionContext.JobContext.Container["id"] = new StringContextData(container.ContainerId); } executionContext.Output("##[endgroup]"); }