From 356034fc4c6b6e08f01aaa7efd4ac291bdd9551c Mon Sep 17 00:00:00 2001 From: David Kale Date: Wed, 5 Jun 2019 17:25:25 -0400 Subject: [PATCH 01/24] Do not require system.defaultworkingdirectory for container job --- src/Misc/layoutbin/en-US/strings.json | 1 - src/Runner.Worker/ContainerOperationProvider.cs | 8 ++------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Misc/layoutbin/en-US/strings.json b/src/Misc/layoutbin/en-US/strings.json index 98c209890b2..eac5b33f45e 100644 --- a/src/Misc/layoutbin/en-US/strings.json +++ b/src/Misc/layoutbin/en-US/strings.json @@ -211,7 +211,6 @@ "ConnectingToServer": "Connecting to server ...", "ConnectSectionHeader": "Connect", "ConnectToServer": "Connecting to the server.", - "ContainerJobRequireSystemDefaultWorkDir": "System.DefaultWorkingDirectory is required for running a container job.", "ContainerWindowsVersionRequirement": "Container feature requires Windows Server 1803 or higher. Please reference documentation (https://go.microsoft.com/fwlink/?linkid=875268)", "CouldNotRemoveService": "Could not delete service '{0}'", "CreateUserWithSameUIDInsideContainer": "Try create an user with UID '{0}' inside the container.", diff --git a/src/Runner.Worker/ContainerOperationProvider.cs b/src/Runner.Worker/ContainerOperationProvider.cs index 9192d7867e2..3782cafd185 100644 --- a/src/Runner.Worker/ContainerOperationProvider.cs +++ b/src/Runner.Worker/ContainerOperationProvider.cs @@ -249,13 +249,9 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Work), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Work)))); container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Tools), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Tools)))); #else - string defaultWorkingDirectory = executionContext.Variables.Get(Constants.Variables.System.DefaultWorkingDirectory); - if (string.IsNullOrEmpty(defaultWorkingDirectory)) - { - throw new NotSupportedException(StringUtil.Loc("ContainerJobRequireSystemDefaultWorkDir")); - } - string workingDirectory = Path.GetDirectoryName(defaultWorkingDirectory.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)); + string workingDirectory = HostContext.GetDirectory(WellKnownDirectory.Work); + // string workingDirectory = Path.GetDirectoryName(defaultWorkingDirectory.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)); container.MountVolumes.Add(new MountVolume(container.TranslateToHostPath(workingDirectory), workingDirectory)); container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Temp), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Temp)))); container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Tools), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Tools)))); From 38f3c6a8bfaafb52e7f97a4498a18c50a6f18dd5 Mon Sep 17 00:00:00 2001 From: David Kale Date: Thu, 6 Jun 2019 15:47:01 -0400 Subject: [PATCH 02/24] Remove containerHandler intermediary --- .../containerHandlerInvoker.js.template | 63 ------------------- src/Runner.Worker/ActionRunner.cs | 2 +- .../ContainerOperationProvider.cs | 2 +- src/Runner.Worker/Handlers/StepHost.cs | 2 +- 4 files changed, 3 insertions(+), 66 deletions(-) delete mode 100644 src/Misc/layoutbin/containerHandlerInvoker.js.template diff --git a/src/Misc/layoutbin/containerHandlerInvoker.js.template b/src/Misc/layoutbin/containerHandlerInvoker.js.template deleted file mode 100644 index 31665951902..00000000000 --- a/src/Misc/layoutbin/containerHandlerInvoker.js.template +++ /dev/null @@ -1,63 +0,0 @@ -const { spawn } = require('child_process'); -var stdinString = ""; -process.stdin.on('data', function (chunk) { - stdinString += chunk; -}); - -process.stdin.on('end', function () { - var stdinData = JSON.parse(stdinString); - var handler = stdinData.handler; - var handlerArg = stdinData.args; - var handlerWorkDir = stdinData.workDir; - var prependPath = stdinData.prependPath; - - console.log("##vso[task.debug]Handler: " + handler); - console.log("##vso[task.debug]HandlerArg: " + handlerArg); - console.log("##vso[task.debug]HandlerWorkDir: " + handlerWorkDir); - Object.keys(stdinData.environment).forEach(function (key) { - console.log("##vso[task.debug]Set env: " + key + "=" + stdinData.environment[key].toString().replace('\r', '%0D').replace('\n', '%0A')); - process.env[key] = stdinData.environment[key]; - }); - - var currentPath = process.env['PATH']; - var options = { - stdio: 'inherit', - cwd: handlerWorkDir - }; - if (process.platform == 'win32') { - options.argv0 = `"${handler}"`; - options.windowsVerbatimArguments = true; - - if (prependPath && prependPath.length > 0) { - if (currentPath && currentPath.length > 0) { - process.env['PATH'] = prependPath + ';' + currentPath; - } - else { - process.env['PATH'] = prependPath; - } - } - } - else { - if (prependPath && prependPath.length > 0) { - if (currentPath && currentPath.length > 0) { - process.env['PATH'] = prependPath + ':' + currentPath; - } - else { - process.env['PATH'] = prependPath; - } - } - } - - if (prependPath && prependPath.length > 0) { - console.log("##vso[task.debug]Prepend Path: " + process.env['PATH']); - } - - process.env['TF_BUILD'] = 'True'; - var launch = spawn(handler, [handlerArg], options); - launch.on('exit', function (code) { - console.log("##vso[task.debug]Handler exit code: " + code); - if (code != 0) { - process.exit(code); - } - }); -}); diff --git a/src/Runner.Worker/ActionRunner.cs b/src/Runner.Worker/ActionRunner.cs index a6f1ba76d4a..d9c991478de 100644 --- a/src/Runner.Worker/ActionRunner.cs +++ b/src/Runner.Worker/ActionRunner.cs @@ -63,7 +63,7 @@ public async Task RunAsync() var containerStepHost = HostContext.CreateService(); containerStepHost.Container = ExecutionContext.Container; stepHost = containerStepHost; - throw new NotSupportedException("Call Ting to fix this."); + //throw new NotSupportedException("Call Ting to fix this."); } // Load the inputs. diff --git a/src/Runner.Worker/ContainerOperationProvider.cs b/src/Runner.Worker/ContainerOperationProvider.cs index 3782cafd185..408c7bb7628 100644 --- a/src/Runner.Worker/ContainerOperationProvider.cs +++ b/src/Runner.Worker/ContainerOperationProvider.cs @@ -281,7 +281,7 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta } else { - node = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), "node", "bin", $"node{IOUtil.ExeExtension}")); + node = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), "node10", "bin", $"node{IOUtil.ExeExtension}")); } string sleepCommand = $"\"{node}\" -e \"setInterval(function(){{}}, 24 * 60 * 60 * 1000);\""; container.ContainerCommand = sleepCommand; diff --git a/src/Runner.Worker/Handlers/StepHost.cs b/src/Runner.Worker/Handlers/StepHost.cs index f8a083e7c55..323ac643c44 100644 --- a/src/Runner.Worker/Handlers/StepHost.cs +++ b/src/Runner.Worker/Handlers/StepHost.cs @@ -159,7 +159,7 @@ public async Task ExecuteAsync(string workingDirectory, } else { - node = Container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), "node", "bin", $"node{IOUtil.ExeExtension}")); + node = Container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), "node10", "bin", $"node{IOUtil.ExeExtension}")); } string entryScript = Container.TranslateToContainerPath(Path.Combine(tempDir, "containerHandlerInvoker.js")); From cb66113bde55525f816248fbdc202227448d1268 Mon Sep 17 00:00:00 2001 From: David Kale Date: Thu, 6 Jun 2019 16:11:53 -0400 Subject: [PATCH 03/24] Send environment to child proc 'docker exec' so it can pick up -e VAR --- src/Runner.Worker/Handlers/StepHost.cs | 51 ++++++++------------------ 1 file changed, 16 insertions(+), 35 deletions(-) diff --git a/src/Runner.Worker/Handlers/StepHost.cs b/src/Runner.Worker/Handlers/StepHost.cs index 323ac643c44..bb9dc1c262d 100644 --- a/src/Runner.Worker/Handlers/StepHost.cs +++ b/src/Runner.Worker/Handlers/StepHost.cs @@ -132,15 +132,6 @@ public async Task ExecuteAsync(string workingDirectory, var dockerManger = HostContext.GetService(); string containerEnginePath = dockerManger.DockerPath; - ContainerStandardInPayload payload = new ContainerStandardInPayload() - { - ExecutionHandler = fileName, - ExecutionHandlerWorkingDirectory = workingDirectory, - ExecutionHandlerArguments = arguments, - ExecutionHandlerEnvironment = environment, - ExecutionHandlerPrependPath = PrependPath - }; - // copy the intermediate script (containerHandlerInvoker.js) into Agent_TempDirectory // Background: // We rely on environment variables to send task execution information from agent to task execution engine (node/powershell) @@ -165,10 +156,21 @@ public async Task ExecuteAsync(string workingDirectory, string entryScript = Container.TranslateToContainerPath(Path.Combine(tempDir, "containerHandlerInvoker.js")); #if !OS_WINDOWS - string containerExecutionArgs = $"exec -i -u {Container.CurrentUserId} {Container.ContainerId} {node} {entryScript}"; + IList containerExecutionArgs = new List(); + containerExecutionArgs.Add("exec"); + containerExecutionArgs.Add($"-i -u {Container.CurrentUserId} {Container.ContainerId}"); #else - string containerExecutionArgs = $"exec -i {Container.ContainerId} {node} {entryScript}"; + IList containerExecutionArgs = new List(); + containerExecutionArgs.Add("exec"); + containerExecutionArgs.Add($"-i {Container.ContainerId}"); #endif + foreach (var env in environment) + { + containerExecutionArgs.Add($"-e \"{env.Key}\""); + } + containerExecutionArgs.Add(fileName); + containerExecutionArgs.Add(arguments); + string containerExecutionArgString = string.Join(" ", containerExecutionArgs); using (var processInvoker = HostContext.CreateService()) { @@ -183,38 +185,17 @@ public async Task ExecuteAsync(string workingDirectory, outputEncoding = null; #endif - var redirectStandardIn = Channel.CreateUnbounded(new UnboundedChannelOptions() { SingleReader = true, SingleWriter = true }); - redirectStandardIn.Writer.TryWrite(JsonUtility.ToString(payload)); - return await processInvoker.ExecuteAsync(workingDirectory: HostContext.GetDirectory(WellKnownDirectory.Work), fileName: containerEnginePath, - arguments: containerExecutionArgs, - environment: null, + arguments: containerExecutionArgString, + environment: environment, requireExitCodeZero: requireExitCodeZero, outputEncoding: outputEncoding, killProcessOnCancel: killProcessOnCancel, - redirectStandardIn: redirectStandardIn, + redirectStandardIn: null, inheritConsoleHandler: inheritConsoleHandler, cancellationToken: cancellationToken); } } - - private class ContainerStandardInPayload - { - [JsonProperty("handler")] - public String ExecutionHandler { get; set; } - - [JsonProperty("args")] - public String ExecutionHandlerArguments { get; set; } - - [JsonProperty("workDir")] - public String ExecutionHandlerWorkingDirectory { get; set; } - - [JsonProperty("environment")] - public IDictionary ExecutionHandlerEnvironment { get; set; } - - [JsonProperty("prependPath")] - public string ExecutionHandlerPrependPath { get; set; } - } } } From 046f663c301debe9584cf15b1ca3354464e4162b Mon Sep 17 00:00:00 2001 From: David Kale Date: Thu, 6 Jun 2019 16:34:22 -0400 Subject: [PATCH 04/24] qualify script path for docker exec --- src/Runner.Worker/Handlers/StepHost.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Runner.Worker/Handlers/StepHost.cs b/src/Runner.Worker/Handlers/StepHost.cs index bb9dc1c262d..5efbf442d17 100644 --- a/src/Runner.Worker/Handlers/StepHost.cs +++ b/src/Runner.Worker/Handlers/StepHost.cs @@ -153,22 +153,20 @@ public async Task ExecuteAsync(string workingDirectory, node = Container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), "node10", "bin", $"node{IOUtil.ExeExtension}")); } - string entryScript = Container.TranslateToContainerPath(Path.Combine(tempDir, "containerHandlerInvoker.js")); + string entryScript = Container.TranslateToContainerPath(Path.Combine(tempDir, fileName)); -#if !OS_WINDOWS IList containerExecutionArgs = new List(); containerExecutionArgs.Add("exec"); - containerExecutionArgs.Add($"-i -u {Container.CurrentUserId} {Container.ContainerId}"); +#if !OS_WINDOWS + containerExecutionArgs.Add($"-u {Container.CurrentUserId}"); #else - IList containerExecutionArgs = new List(); - containerExecutionArgs.Add("exec"); - containerExecutionArgs.Add($"-i {Container.ContainerId}"); #endif + containerExecutionArgs.Add($"-i {Container.ContainerId}"); foreach (var env in environment) { containerExecutionArgs.Add($"-e \"{env.Key}\""); } - containerExecutionArgs.Add(fileName); + containerExecutionArgs.Add(entryScript); containerExecutionArgs.Add(arguments); string containerExecutionArgString = string.Join(" ", containerExecutionArgs); From 3c2cc8f319af3fe38e60c732177e7ffd3710f6fc Mon Sep 17 00:00:00 2001 From: David Kale Date: Thu, 6 Jun 2019 17:20:49 -0400 Subject: [PATCH 05/24] exec script file directly --- src/Runner.Worker/Handlers/ScriptHandler.cs | 6 +++--- src/Runner.Worker/Handlers/StepHost.cs | 15 ++++++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Runner.Worker/Handlers/ScriptHandler.cs b/src/Runner.Worker/Handlers/ScriptHandler.cs index bdad2b71b40..db4d020ba5e 100644 --- a/src/Runner.Worker/Handlers/ScriptHandler.cs +++ b/src/Runner.Worker/Handlers/ScriptHandler.cs @@ -65,6 +65,9 @@ public async Task RunAsync() // Arguments var arguments = $"/D /E:ON /V:OFF /S /C \"CALL \"{StepHost.ResolvePathForStepHost(filePath)}\"\""; #else + // Command path + var commandPath = WhichUtil.Which("bash") ?? WhichUtil.Which("sh", true); + // Fixup contents contents = $"set -eo pipefail\n{contents}"; @@ -73,9 +76,6 @@ public async Task RunAsync() // Don't add a BOM. It causes the script to fail on some operating systems (e.g. on Ubuntu 14). File.WriteAllText(filePath, contents, new UTF8Encoding(false)); - // Command path - var commandPath = WhichUtil.Which("bash") ?? WhichUtil.Which("sh", true); - // Arguments var arguments = $"--noprofile --norc {StepHost.ResolvePathForStepHost(filePath).Replace("\"", "\\\"")}"; #endif diff --git a/src/Runner.Worker/Handlers/StepHost.cs b/src/Runner.Worker/Handlers/StepHost.cs index 5efbf442d17..a96492047b2 100644 --- a/src/Runner.Worker/Handlers/StepHost.cs +++ b/src/Runner.Worker/Handlers/StepHost.cs @@ -155,18 +155,23 @@ public async Task ExecuteAsync(string workingDirectory, string entryScript = Container.TranslateToContainerPath(Path.Combine(tempDir, fileName)); + // Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...] IList containerExecutionArgs = new List(); - containerExecutionArgs.Add("exec"); + + containerExecutionArgs.Add($"exec -i"); #if !OS_WINDOWS - containerExecutionArgs.Add($"-u {Container.CurrentUserId}"); + if (!String.IsNullOrEmpty(Container.CurrentUserName)) + { + containerExecutionArgs.Add($"-u {Container.CurrentUserId}"); + } #else #endif - containerExecutionArgs.Add($"-i {Container.ContainerId}"); foreach (var env in environment) { - containerExecutionArgs.Add($"-e \"{env.Key}\""); + containerExecutionArgs.Add($"-e {env.Key}"); } - containerExecutionArgs.Add(entryScript); + containerExecutionArgs.Add($"{Container.ContainerId}"); + containerExecutionArgs.Add(fileName); containerExecutionArgs.Add(arguments); string containerExecutionArgString = string.Join(" ", containerExecutionArgs); From 34e608c825a6ba3f2fef4fb6cfde0a971a34285b Mon Sep 17 00:00:00 2001 From: David Kale Date: Fri, 7 Jun 2019 16:04:22 -0400 Subject: [PATCH 06/24] Exec run script directly, map env into exec process, chain loaded into script --- src/Runner.Worker/Handlers/StepHost.cs | 57 ++++++++++---------------- 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/src/Runner.Worker/Handlers/StepHost.cs b/src/Runner.Worker/Handlers/StepHost.cs index a96492047b2..b61d0a88900 100644 --- a/src/Runner.Worker/Handlers/StepHost.cs +++ b/src/Runner.Worker/Handlers/StepHost.cs @@ -130,50 +130,37 @@ public async Task ExecuteAsync(string workingDirectory, ArgUtil.NotNullOrEmpty(Container.ContainerId, nameof(Container.ContainerId)); var dockerManger = HostContext.GetService(); - string containerEnginePath = dockerManger.DockerPath; - - // copy the intermediate script (containerHandlerInvoker.js) into Agent_TempDirectory - // Background: - // We rely on environment variables to send task execution information from agent to task execution engine (node/powershell) - // Those task execution information will include all the variables and secrets customer has. - // The only way to pass environment variables to `docker exec` is through command line arguments, ex: `docker exec -e myenv=myvalue -e mysecert=mysecretvalue ...` - // Since command execution may get log into system event log which might cause secret leaking. - // We use this intermediate script to read everything from STDIN, then launch the task execution engine (node/powershell) and redirect STDOUT/STDERR - - string tempDir = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), Constants.Path.TempDirectory); - File.Copy(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Bin), "containerHandlerInvoker.js.template"), Path.Combine(tempDir, "containerHandlerInvoker.js"), true); - - string node; - if (!string.IsNullOrEmpty(Container.ContainerBringNodePath)) - { - node = Container.ContainerBringNodePath; - } - else - { - node = Container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), "node10", "bin", $"node{IOUtil.ExeExtension}")); - } - - string entryScript = Container.TranslateToContainerPath(Path.Combine(tempDir, fileName)); + string dockerClientPath = dockerManger.DockerPath; // Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...] - IList containerExecutionArgs = new List(); + IList dockerCommandArgs = new List(); + dockerCommandArgs.Add($"exec"); - containerExecutionArgs.Add($"exec -i"); + // [OPTIONS] + dockerCommandArgs.Add($"-i"); #if !OS_WINDOWS if (!String.IsNullOrEmpty(Container.CurrentUserName)) { - containerExecutionArgs.Add($"-u {Container.CurrentUserId}"); + dockerCommandArgs.Add($"-u {Container.CurrentUserId}"); } -#else #endif foreach (var env in environment) { - containerExecutionArgs.Add($"-e {env.Key}"); + // e.g. -e MY_SECRET maps the value into the exec'ed process without exposing + // the value directly in the command + dockerCommandArgs.Add($"-e {env.Key}"); } - containerExecutionArgs.Add($"{Container.ContainerId}"); - containerExecutionArgs.Add(fileName); - containerExecutionArgs.Add(arguments); - string containerExecutionArgString = string.Join(" ", containerExecutionArgs); + + // CONTAINER + dockerCommandArgs.Add($"{Container.ContainerId}"); + + // COMMAND + dockerCommandArgs.Add(fileName); + + // [ARG...] + dockerCommandArgs.Add(arguments); + + string dockerCommandArgstring = string.Join(" ", dockerCommandArgs); using (var processInvoker = HostContext.CreateService()) { @@ -189,8 +176,8 @@ public async Task ExecuteAsync(string workingDirectory, #endif return await processInvoker.ExecuteAsync(workingDirectory: HostContext.GetDirectory(WellKnownDirectory.Work), - fileName: containerEnginePath, - arguments: containerExecutionArgString, + fileName: dockerClientPath, + arguments: dockerCommandArgstring, environment: environment, requireExitCodeZero: requireExitCodeZero, outputEncoding: outputEncoding, From d6e333bbeaf5765513b661c9ab0133e33d52b624 Mon Sep 17 00:00:00 2001 From: David Kale Date: Fri, 7 Jun 2019 17:32:05 -0400 Subject: [PATCH 07/24] Cleanup --- src/Runner.Worker/ActionRunner.cs | 1 - src/Runner.Worker/ContainerOperationProvider.cs | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Runner.Worker/ActionRunner.cs b/src/Runner.Worker/ActionRunner.cs index d9c991478de..4376cfa6bfe 100644 --- a/src/Runner.Worker/ActionRunner.cs +++ b/src/Runner.Worker/ActionRunner.cs @@ -63,7 +63,6 @@ public async Task RunAsync() var containerStepHost = HostContext.CreateService(); containerStepHost.Container = ExecutionContext.Container; stepHost = containerStepHost; - //throw new NotSupportedException("Call Ting to fix this."); } // Load the inputs. diff --git a/src/Runner.Worker/ContainerOperationProvider.cs b/src/Runner.Worker/ContainerOperationProvider.cs index 408c7bb7628..02164c21dee 100644 --- a/src/Runner.Worker/ContainerOperationProvider.cs +++ b/src/Runner.Worker/ContainerOperationProvider.cs @@ -250,8 +250,8 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Tools), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Tools)))); #else + // TODO @dakale string workingDirectory = HostContext.GetDirectory(WellKnownDirectory.Work); - // string workingDirectory = Path.GetDirectoryName(defaultWorkingDirectory.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)); container.MountVolumes.Add(new MountVolume(container.TranslateToHostPath(workingDirectory), workingDirectory)); container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Temp), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Temp)))); container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Tools), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Tools)))); @@ -284,6 +284,7 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta node = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), "node10", "bin", $"node{IOUtil.ExeExtension}")); } string sleepCommand = $"\"{node}\" -e \"setInterval(function(){{}}, 24 * 60 * 60 * 1000);\""; + container.ContainerCommand = sleepCommand; } From 4ce8226e8294e1f268890630f428cc04f1ffecdb Mon Sep 17 00:00:00 2001 From: David Kale Date: Mon, 10 Jun 2019 13:19:30 -0400 Subject: [PATCH 08/24] Always use entrypoint + args instead of command for running container --- src/Runner.Worker/Container/ContainerInfo.cs | 3 +-- .../Container/DockerCommandManager.cs | 19 +++++++++++++++---- .../ContainerOperationProvider.cs | 5 ++--- .../Handlers/ContainerActionHandler.cs | 2 +- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/Runner.Worker/Container/ContainerInfo.cs b/src/Runner.Worker/Container/ContainerInfo.cs index 2a76aaec8d0..69b62c87420 100644 --- a/src/Runner.Worker/Container/ContainerInfo.cs +++ b/src/Runner.Worker/Container/ContainerInfo.cs @@ -41,7 +41,6 @@ public ContainerInfo(IHostContext hostContext, Pipelines.ContainerResource conta this.SkipContainerImagePull = container.Properties.Get("localimage"); _environmentVariables = container.Environment; this.ContainerEntryPoint = container.Properties.Get("entrypoint", defaultValue: ""); - this.ContainerCommand = container.Properties.Get("command", defaultValue: ""); this.ContainerWorkDirectory = container.Properties.Get("workdir", defaultValue: ""); this.IsJobContainer = isJobContainer; @@ -81,8 +80,8 @@ public ContainerInfo(IHostContext hostContext, Pipelines.ContainerResource conta public string ContainerNetworkAlias { get; set; } public string ContainerImage { get; set; } public string ContainerName { get; set; } + public string ContainerEntryPointArgs { get; set; } public string ContainerEntryPoint { get; set; } - public string ContainerCommand { get; set; } public string ContainerWorkDirectory { get; set; } public string ContainerBringNodePath { get; set; } public Guid ContainerRegistryEndpoint { get; private set; } diff --git a/src/Runner.Worker/Container/DockerCommandManager.cs b/src/Runner.Worker/Container/DockerCommandManager.cs index fe1b0a28153..45a8ed172f5 100644 --- a/src/Runner.Worker/Container/DockerCommandManager.cs +++ b/src/Runner.Worker/Container/DockerCommandManager.cs @@ -132,9 +132,8 @@ public async Task DockerCreate(IExecutionContext context, ContainerInfo dockerOptions.Add($"{container.ContainerCreateOptions}"); foreach (var env in container.ContainerEnvironmentVariables) { - if (String.IsNullOrEmpty(env.Value) && String.IsNullOrEmpty(context.Variables.Get("_VSTS_DONT_RESOLVE_ENV_FROM_HOST"))) + if (String.IsNullOrEmpty(env.Value)) { - // TODO: Remove fallback variable if stable dockerOptions.Add($"-e \"{env.Key}\""); } else @@ -162,10 +161,18 @@ public async Task DockerCreate(IExecutionContext context, ContainerInfo } dockerOptions.Add(volumeArg); } + if (!string.IsNullOrEmpty(container.ContainerEntryPoint)) + { + dockerOptions.Add($"--entrypoint \"{container.ContainerEntryPoint}\""); + } // IMAGE dockerOptions.Add($"{container.ContainerImage}"); + // COMMAND - dockerOptions.Add($"{container.ContainerCommand}"); + // Intentionally blank. Always overwrite ENTRYPOINT and send ARGs + + // [ARG...] + dockerOptions.Add($"{container.ContainerEntryPointArgs}"); var optionsString = string.Join(" ", dockerOptions); List outputStrings = await ExecuteDockerCommandAsync(context, "create", optionsString); @@ -219,8 +226,12 @@ public async Task DockerRun(IExecutionContext context, ContainerInfo contai } // IMAGE dockerOptions.Add($"{container.ContainerImage}"); + // COMMAND - dockerOptions.Add($"{container.ContainerCommand}"); + // Intentionally blank. Always overwrite ENTRYPOINT and send ARGs + + // [ARG...] + dockerOptions.Add($"{container.ContainerEntryPointArgs}"); var optionsString = string.Join(" ", dockerOptions); return await ExecuteDockerCommandAsync(context, "run", optionsString, context.CancellationToken); diff --git a/src/Runner.Worker/ContainerOperationProvider.cs b/src/Runner.Worker/ContainerOperationProvider.cs index 02164c21dee..58d2bf73a25 100644 --- a/src/Runner.Worker/ContainerOperationProvider.cs +++ b/src/Runner.Worker/ContainerOperationProvider.cs @@ -283,9 +283,8 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta { node = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), "node10", "bin", $"node{IOUtil.ExeExtension}")); } - string sleepCommand = $"\"{node}\" -e \"setInterval(function(){{}}, 24 * 60 * 60 * 1000);\""; - - container.ContainerCommand = sleepCommand; + container.ContainerEntryPoint = node; + container.ContainerEntryPointArgs = $"-e \"setInterval(function(){{}}, 24 * 60 * 60 * 1000);\""; } container.ContainerId = await _dockerManger.DockerCreate(executionContext, container); diff --git a/src/Runner.Worker/Handlers/ContainerActionHandler.cs b/src/Runner.Worker/Handlers/ContainerActionHandler.cs index 9f8963c8553..bc7ea3cd70b 100644 --- a/src/Runner.Worker/Handlers/ContainerActionHandler.cs +++ b/src/Runner.Worker/Handlers/ContainerActionHandler.cs @@ -57,7 +57,7 @@ public async Task RunAsync() }; container.ContainerEntryPoint = Inputs.GetValueOrDefault("entryPoint"); - container.ContainerCommand = Inputs.GetValueOrDefault("args"); + container.ContainerEntryPointArgs = Inputs.GetValueOrDefault("args"); if (ExecutionContext.Variables.TryGetValue("Agent.ContainerNetwork", out string containerNetwork)) { From 463ea2258740689a6505add4b2b0d4ee634815cf Mon Sep 17 00:00:00 2001 From: David Kale Date: Tue, 11 Jun 2019 15:20:37 -0400 Subject: [PATCH 09/24] Use --workdir --- src/Runner.Worker/Container/ContainerInfo.cs | 6 ++++-- src/Runner.Worker/Container/DockerCommandManager.cs | 4 ++++ src/Runner.Worker/ContainerOperationProvider.cs | 3 +-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Runner.Worker/Container/ContainerInfo.cs b/src/Runner.Worker/Container/ContainerInfo.cs index 69b62c87420..ff7ad53e5c6 100644 --- a/src/Runner.Worker/Container/ContainerInfo.cs +++ b/src/Runner.Worker/Container/ContainerInfo.cs @@ -45,13 +45,15 @@ public ContainerInfo(IHostContext hostContext, Pipelines.ContainerResource conta this.IsJobContainer = isJobContainer; #if OS_WINDOWS + this.ContainerWorkDirectory = "C:\\__w"; + _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Work)] = this.ContainerWorkDirectory; _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Tools)] = "C:\\__t"; // Tool cache folder may come from ENV, so we need a unique folder to avoid collision - _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Work)] = "C:\\__w"; _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Root)] = "C:\\__a"; // add -v '\\.\pipe\docker_engine:\\.\pipe\docker_engine' when they are available (17.09) #else + this.ContainerWorkDirectory = "/__w"; + _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Work)] = this.ContainerWorkDirectory; _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Tools)] = "/__t"; // Tool cache folder may come from ENV, so we need a unique folder to avoid collision - _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Work)] = "/__w"; _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Root)] = "/__a"; if (this.IsJobContainer) { diff --git a/src/Runner.Worker/Container/DockerCommandManager.cs b/src/Runner.Worker/Container/DockerCommandManager.cs index 45a8ed172f5..322ae0a13ec 100644 --- a/src/Runner.Worker/Container/DockerCommandManager.cs +++ b/src/Runner.Worker/Container/DockerCommandManager.cs @@ -117,6 +117,10 @@ public async Task DockerCreate(IExecutionContext context, ContainerInfo // OPTIONS dockerOptions.Add($"--name {container.ContainerDisplayName}"); dockerOptions.Add($"--label {DockerInstanceLabel}"); + if (!string.IsNullOrEmpty(container.ContainerWorkDirectory)) + { + dockerOptions.Add($"--workdir {container.ContainerWorkDirectory}"); + } if (!string.IsNullOrEmpty(container.ContainerNetwork)) { dockerOptions.Add($"--network {container.ContainerNetwork}"); diff --git a/src/Runner.Worker/ContainerOperationProvider.cs b/src/Runner.Worker/ContainerOperationProvider.cs index 58d2bf73a25..87cee79030a 100644 --- a/src/Runner.Worker/ContainerOperationProvider.cs +++ b/src/Runner.Worker/ContainerOperationProvider.cs @@ -250,9 +250,8 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Tools), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Tools)))); #else - // TODO @dakale string workingDirectory = HostContext.GetDirectory(WellKnownDirectory.Work); - container.MountVolumes.Add(new MountVolume(container.TranslateToHostPath(workingDirectory), workingDirectory)); + container.MountVolumes.Add(new MountVolume(container.TranslateToHostPath(workingDirectory), container.TranslateToContainerPath(workingDirectory))); container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Temp), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Temp)))); container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Tools), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Tools)))); container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Actions), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Actions)))); From 5b37ba2c4e3033c11ec047bc84efefb2bbf16b41 Mon Sep 17 00:00:00 2001 From: David Kale Date: Tue, 11 Jun 2019 15:38:51 -0400 Subject: [PATCH 10/24] Remove old container user / group handling --- .../ContainerOperationProvider.cs | 147 ------------------ src/Runner.Worker/Handlers/ScriptHandler.cs | 6 +- 2 files changed, 3 insertions(+), 150 deletions(-) diff --git a/src/Runner.Worker/ContainerOperationProvider.cs b/src/Runner.Worker/ContainerOperationProvider.cs index 87cee79030a..82a812386e9 100644 --- a/src/Runner.Worker/ContainerOperationProvider.cs +++ b/src/Runner.Worker/ContainerOperationProvider.cs @@ -348,153 +348,6 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta $"{port.HostPort}"); } } - -#if !OS_WINDOWS - if (container.IsJobContainer) - { - // Ensure bash exist in the image - int execWhichBashExitCode = await _dockerManger.DockerExec(executionContext, container.ContainerId, string.Empty, $"sh -c \"command -v bash\""); - if (execWhichBashExitCode != 0) - { - throw new InvalidOperationException($"Docker exec fail with exit code {execWhichBashExitCode}"); - } - - // Get current username - container.CurrentUserName = (await ExecuteCommandAsync(executionContext, "whoami", string.Empty)).FirstOrDefault(); - ArgUtil.NotNullOrEmpty(container.CurrentUserName, nameof(container.CurrentUserName)); - - // Get current userId - container.CurrentUserId = (await ExecuteCommandAsync(executionContext, "id", $"-u {container.CurrentUserName}")).FirstOrDefault(); - ArgUtil.NotNullOrEmpty(container.CurrentUserId, nameof(container.CurrentUserId)); - - executionContext.Output(StringUtil.Loc("CreateUserWithSameUIDInsideContainer", container.CurrentUserId)); - - // Create an user with same uid as the agent run as user inside the container. - // All command execute in docker will run as Root by default, - // this will cause the agent on the host machine doesn't have permission to any new file/folder created inside the container. - // So, we create a user account with same UID inside the container and let all docker exec command run as that user. - string containerUserName = string.Empty; - - // We need to find out whether there is a user with same UID inside the container - List userNames = new List(); - int execGrepExitCode = await _dockerManger.DockerExec(executionContext, container.ContainerId, string.Empty, $"bash -c \"grep {container.CurrentUserId} /etc/passwd | cut -f1 -d:\"", userNames); - if (execGrepExitCode != 0) - { - throw new InvalidOperationException($"Docker exec fail with exit code {execGrepExitCode}"); - } - - if (userNames.Count > 0) - { - // check all potential username that might match the UID. - foreach (string username in userNames) - { - int execIdExitCode = await _dockerManger.DockerExec(executionContext, container.ContainerId, string.Empty, $"id -u {username}"); - if (execIdExitCode == 0) - { - containerUserName = username; - break; - } - } - } - - // Create a new user with same UID - if (string.IsNullOrEmpty(containerUserName)) - { - containerUserName = $"{container.CurrentUserName}_azpcontainer"; - int execUseraddExitCode = await _dockerManger.DockerExec(executionContext, container.ContainerId, string.Empty, $"useradd -m -u {container.CurrentUserId} {containerUserName}"); - if (execUseraddExitCode != 0) - { - throw new InvalidOperationException($"Docker exec fail with exit code {execUseraddExitCode}"); - } - } - - executionContext.Output(StringUtil.Loc("GrantContainerUserSUDOPrivilege", containerUserName)); - - // Create a new group for giving sudo permission - int execGroupaddExitCode = await _dockerManger.DockerExec(executionContext, container.ContainerId, string.Empty, $"groupadd azure_pipelines_sudo"); - if (execGroupaddExitCode != 0) - { - throw new InvalidOperationException($"Docker exec fail with exit code {execGroupaddExitCode}"); - } - - // Add the new created user to the new created sudo group. - int execUsermodExitCode = await _dockerManger.DockerExec(executionContext, container.ContainerId, string.Empty, $"usermod -a -G azure_pipelines_sudo {containerUserName}"); - if (execUsermodExitCode != 0) - { - throw new InvalidOperationException($"Docker exec fail with exit code {execUsermodExitCode}"); - } - - // Allow the new sudo group run any sudo command without providing password. - int execEchoExitCode = await _dockerManger.DockerExec(executionContext, container.ContainerId, string.Empty, $"su -c \"echo '%azure_pipelines_sudo ALL=(ALL:ALL) NOPASSWD:ALL' >> /etc/sudoers\""); - if (execUsermodExitCode != 0) - { - throw new InvalidOperationException($"Docker exec fail with exit code {execEchoExitCode}"); - } - - bool setupDockerGroup = executionContext.Variables.GetBoolean("VSTS_SETUP_DOCKERGROUP") ?? StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("VSTS_SETUP_DOCKERGROUP"), true); - if (setupDockerGroup) - { - executionContext.Output(StringUtil.Loc("AllowContainerUserRunDocker", containerUserName)); - // Get docker.sock group id on Host - string dockerSockGroupId = (await ExecuteCommandAsync(executionContext, "stat", $"-c %g /var/run/docker.sock")).FirstOrDefault(); - - // We need to find out whether there is a group with same GID inside the container - string existingGroupName = null; - List groupsOutput = new List(); - int execGroupGrepExitCode = await _dockerManger.DockerExec(executionContext, container.ContainerId, string.Empty, $"bash -c \"cat /etc/group\"", groupsOutput); - if (execGroupGrepExitCode != 0) - { - throw new InvalidOperationException($"Docker exec fail with exit code {execGroupGrepExitCode}"); - } - - if (groupsOutput.Count > 0) - { - // check all potential groups that might match the GID. - foreach (string groupOutput in groupsOutput) - { - if (!string.IsNullOrEmpty(groupOutput)) - { - var groupSegments = groupOutput.Split(':'); - if (groupSegments.Length != 4) - { - Trace.Warning($"Unexpected output from /etc/group: '{groupOutput}'"); - } - else - { - // the output of /etc/group should looks like `group:x:gid:` - var groupName = groupSegments[0]; - var groupId = groupSegments[2]; - - if (string.Equals(dockerSockGroupId, groupId)) - { - existingGroupName = groupName; - break; - } - } - } - } - } - - if (string.IsNullOrEmpty(existingGroupName)) - { - // create a new group with same gid - existingGroupName = "azure_pipelines_docker"; - int execDockerGroupaddExitCode = await _dockerManger.DockerExec(executionContext, container.ContainerId, string.Empty, $"groupadd -g {dockerSockGroupId} azure_pipelines_docker"); - if (execDockerGroupaddExitCode != 0) - { - throw new InvalidOperationException($"Docker exec fail with exit code {execDockerGroupaddExitCode}"); - } - } - - // Add the new created user to the docker socket group. - int execGroupUsermodExitCode = await _dockerManger.DockerExec(executionContext, container.ContainerId, string.Empty, $"usermod -a -G {existingGroupName} {containerUserName}"); - if (execGroupUsermodExitCode != 0) - { - throw new InvalidOperationException($"Docker exec fail with exit code {execGroupUsermodExitCode}"); - } - } - } -#endif } private async Task StopContainerAsync(IExecutionContext executionContext, ContainerInfo container) diff --git a/src/Runner.Worker/Handlers/ScriptHandler.cs b/src/Runner.Worker/Handlers/ScriptHandler.cs index db4d020ba5e..bdad2b71b40 100644 --- a/src/Runner.Worker/Handlers/ScriptHandler.cs +++ b/src/Runner.Worker/Handlers/ScriptHandler.cs @@ -65,9 +65,6 @@ public async Task RunAsync() // Arguments var arguments = $"/D /E:ON /V:OFF /S /C \"CALL \"{StepHost.ResolvePathForStepHost(filePath)}\"\""; #else - // Command path - var commandPath = WhichUtil.Which("bash") ?? WhichUtil.Which("sh", true); - // Fixup contents contents = $"set -eo pipefail\n{contents}"; @@ -76,6 +73,9 @@ public async Task RunAsync() // Don't add a BOM. It causes the script to fail on some operating systems (e.g. on Ubuntu 14). File.WriteAllText(filePath, contents, new UTF8Encoding(false)); + // Command path + var commandPath = WhichUtil.Which("bash") ?? WhichUtil.Which("sh", true); + // Arguments var arguments = $"--noprofile --norc {StepHost.ResolvePathForStepHost(filePath).Replace("\"", "\\\"")}"; #endif From c958fe8872ac22126c2764555b83ac55408be49f Mon Sep 17 00:00:00 2001 From: David Kale Date: Tue, 11 Jun 2019 16:53:26 -0400 Subject: [PATCH 11/24] Fix some formatting and branding --- src/Runner.Worker/Container/ContainerInfo.cs | 2 +- .../Container/DockerCommandManager.cs | 8 ++++---- src/Runner.Worker/ExecutionContext.cs | 20 ++----------------- 3 files changed, 7 insertions(+), 23 deletions(-) diff --git a/src/Runner.Worker/Container/ContainerInfo.cs b/src/Runner.Worker/Container/ContainerInfo.cs index ff7ad53e5c6..42259813cfe 100644 --- a/src/Runner.Worker/Container/ContainerInfo.cs +++ b/src/Runner.Worker/Container/ContainerInfo.cs @@ -51,7 +51,7 @@ public ContainerInfo(IHostContext hostContext, Pipelines.ContainerResource conta _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Root)] = "C:\\__a"; // add -v '\\.\pipe\docker_engine:\\.\pipe\docker_engine' when they are available (17.09) #else - this.ContainerWorkDirectory = "/__w"; + this.ContainerWorkDirectory = "/__w"; _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Work)] = this.ContainerWorkDirectory; _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Tools)] = "/__t"; // Tool cache folder may come from ENV, so we need a unique folder to avoid collision _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Root)] = "/__a"; diff --git a/src/Runner.Worker/Container/DockerCommandManager.cs b/src/Runner.Worker/Container/DockerCommandManager.cs index 322ae0a13ec..e0fc3cbef4d 100644 --- a/src/Runner.Worker/Container/DockerCommandManager.cs +++ b/src/Runner.Worker/Container/DockerCommandManager.cs @@ -117,10 +117,10 @@ public async Task DockerCreate(IExecutionContext context, ContainerInfo // OPTIONS dockerOptions.Add($"--name {container.ContainerDisplayName}"); dockerOptions.Add($"--label {DockerInstanceLabel}"); - if (!string.IsNullOrEmpty(container.ContainerWorkDirectory)) - { - dockerOptions.Add($"--workdir {container.ContainerWorkDirectory}"); - } + if (!string.IsNullOrEmpty(container.ContainerWorkDirectory)) + { + dockerOptions.Add($"--workdir {container.ContainerWorkDirectory}"); + } if (!string.IsNullOrEmpty(container.ContainerNetwork)) { dockerOptions.Add($"--network {container.ContainerNetwork}"); diff --git a/src/Runner.Worker/ExecutionContext.cs b/src/Runner.Worker/ExecutionContext.cs index b28e634199d..d444c59d7d9 100644 --- a/src/Runner.Worker/ExecutionContext.cs +++ b/src/Runner.Worker/ExecutionContext.cs @@ -603,24 +603,8 @@ public void InitializeJob(Pipelines.AgentJobRequestMessage message, Cancellation PrependPath = new List(); // Docker (JobContainer) - string imageName = Variables.Get("_PREVIEW_VSTS_DOCKER_IMAGE"); - if (string.IsNullOrEmpty(imageName)) - { - imageName = Environment.GetEnvironmentVariable("_PREVIEW_VSTS_DOCKER_IMAGE"); - } - - var containerNetwork = $"vsts_network_{Guid.NewGuid().ToString("N")}"; - if (!string.IsNullOrEmpty(imageName) && - string.IsNullOrEmpty(message.JobContainer)) - { - var dockerContainer = new Pipelines.ContainerResource() - { - Alias = "vsts_container_preview" - }; - dockerContainer.Properties.Set("image", imageName); - Container = new ContainerInfo(HostContext, dockerContainer) { ContainerNetwork = containerNetwork }; - } - else if (!string.IsNullOrEmpty(message.JobContainer)) + var containerNetwork = $"runner_network_{Guid.NewGuid().ToString("N")}"; + if (!string.IsNullOrEmpty(message.JobContainer)) { Container = new ContainerInfo(HostContext, message.Resources.Containers.Single(x => string.Equals(x.Alias, message.JobContainer, StringComparison.OrdinalIgnoreCase))) { ContainerNetwork = containerNetwork }; } From c729653a0dac2a1e5e5106b3863149fa25bc5427 Mon Sep 17 00:00:00 2001 From: David Kale Date: Wed, 12 Jun 2019 16:38:36 -0400 Subject: [PATCH 12/24] Remove old code --- src/Runner.Worker/Container/ContainerInfo.cs | 8 - .../ContainerOperationProvider.cs | 153 ++++-------------- src/Runner.Worker/Handlers/StepHost.cs | 6 - 3 files changed, 32 insertions(+), 135 deletions(-) diff --git a/src/Runner.Worker/Container/ContainerInfo.cs b/src/Runner.Worker/Container/ContainerInfo.cs index d4a88403de4..608a26bff43 100644 --- a/src/Runner.Worker/Container/ContainerInfo.cs +++ b/src/Runner.Worker/Container/ContainerInfo.cs @@ -36,9 +36,7 @@ public ContainerInfo(IHostContext hostContext, Pipelines.ContainerResource conta this.ContainerImage = containerImage; this.ContainerDisplayName = $"{container.Alias}_{Pipelines.Validation.NameValidation.Sanitize(containerImage)}_{Guid.NewGuid().ToString("N").Substring(0, 6)}"; - this.ContainerRegistryEndpoint = container.Endpoint?.Id ?? Guid.Empty; this.ContainerCreateOptions = container.Properties.Get("options"); - this.SkipContainerImagePull = container.Properties.Get("localimage"); _environmentVariables = container.Environment; this.ContainerEntryPoint = container.Properties.Get("entrypoint", defaultValue: ""); this.ContainerWorkDirectory = container.Properties.Get("workdir", defaultValue: ""); @@ -86,13 +84,7 @@ public ContainerInfo(IHostContext hostContext, Pipelines.ContainerResource conta public string ContainerEntryPoint { get; set; } public string ContainerWorkDirectory { get; set; } public string ContainerBringNodePath { get; set; } - public Guid ContainerRegistryEndpoint { get; private set; } public string ContainerCreateOptions { get; private set; } - public bool SkipContainerImagePull { get; private set; } -#if !OS_WINDOWS - public string CurrentUserName { get; set; } - public string CurrentUserId { get; set; } -#endif public bool IsJobContainer { get; set; } public IDictionary ContainerEnvironmentVariables diff --git a/src/Runner.Worker/ContainerOperationProvider.cs b/src/Runner.Worker/ContainerOperationProvider.cs index 65e9c6c939d..1bdb1e1c9e9 100644 --- a/src/Runner.Worker/ContainerOperationProvider.cs +++ b/src/Runner.Worker/ContainerOperationProvider.cs @@ -163,9 +163,7 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta Trace.Info($"Container name: {container.ContainerName}"); Trace.Info($"Container image: {container.ContainerImage}"); - Trace.Info($"Container registry: {container.ContainerRegistryEndpoint.ToString()}"); Trace.Info($"Container options: {container.ContainerCreateOptions}"); - Trace.Info($"Skip container image pull: {container.SkipContainerImagePull}"); foreach (var port in container.UserPortMappings) { Trace.Info($"User provided port: {port.Value}"); @@ -175,136 +173,49 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta Trace.Info($"User provided volume: {volume.Value}"); } - // Login to private docker registry - string registryServer = string.Empty; - if (container.ContainerRegistryEndpoint != Guid.Empty) - { - var registryEndpoint = executionContext.Endpoints.FirstOrDefault(x => x.Type == "dockerregistry" && x.Id == container.ContainerRegistryEndpoint); - ArgUtil.NotNull(registryEndpoint, nameof(registryEndpoint)); - - string username = string.Empty; - string password = string.Empty; - registryEndpoint.Authorization?.Parameters?.TryGetValue("registry", out registryServer); - registryEndpoint.Authorization?.Parameters?.TryGetValue("username", out username); - registryEndpoint.Authorization?.Parameters?.TryGetValue("password", out password); - - ArgUtil.NotNullOrEmpty(registryServer, nameof(registryServer)); - ArgUtil.NotNullOrEmpty(username, nameof(username)); - ArgUtil.NotNullOrEmpty(password, nameof(password)); - - int loginExitCode = await _dockerManger.DockerLogin(executionContext, registryServer, username, password); - if (loginExitCode != 0) - { - throw new InvalidOperationException($"Docker login fail with exit code {loginExitCode}"); - } - } - - try - { - if (!container.SkipContainerImagePull) - { - if (!string.IsNullOrEmpty(registryServer) && - registryServer.IndexOf("index.docker.io", StringComparison.OrdinalIgnoreCase) < 0) - { - var registryServerUri = new Uri(registryServer); - if (!container.ContainerImage.StartsWith(registryServerUri.Authority, StringComparison.OrdinalIgnoreCase)) - { - container.ContainerImage = $"{registryServerUri.Authority}/{container.ContainerImage}"; - } - } - - // 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}"); - } - } - - // Mount folder into container + // Mount folder into container #if OS_WINDOWS - container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Externals), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals)))); - container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Work), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Work)))); - container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Tools), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Tools)))); + container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Externals), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals)))); + container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Work), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Work)))); + container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Tools), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Tools)))); #else - string workingDirectory = HostContext.GetDirectory(WellKnownDirectory.Work); - container.MountVolumes.Add(new MountVolume(container.TranslateToHostPath(workingDirectory), container.TranslateToContainerPath(workingDirectory))); - container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Temp), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Temp)))); - container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Tools), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Tools)))); - container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Actions), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Actions)))); - container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Externals), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals)), true)); - - // Ensure .taskkey file exist so we can mount it. - string taskKeyFile = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), ".taskkey"); - if (!File.Exists(taskKeyFile)) - { - File.WriteAllText(taskKeyFile, string.Empty); - } - container.MountVolumes.Add(new MountVolume(taskKeyFile, container.TranslateToContainerPath(taskKeyFile))); + string workingDirectory = HostContext.GetDirectory(WellKnownDirectory.Work); + container.MountVolumes.Add(new MountVolume(container.TranslateToHostPath(workingDirectory), container.TranslateToContainerPath(workingDirectory))); + container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Temp), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Temp)))); + container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Tools), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Tools)))); + container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Actions), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Actions)))); + container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Externals), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals)), true)); #endif - if (container.IsJobContainer) - { - // See if this container brings its own Node.js - container.ContainerBringNodePath = await _dockerManger.DockerInspect(context: executionContext, - dockerObject: container.ContainerImage, - options: $"--format=\"{{{{index .Config.Labels \\\"{_nodeJsPathLabel}\\\"}}}}\""); + if (container.IsJobContainer) + { + // See if this container brings its own Node.js + container.ContainerBringNodePath = await _dockerManger.DockerInspect(context: executionContext, + dockerObject: container.ContainerImage, + options: $"--format=\"{{{{index .Config.Labels \\\"{_nodeJsPathLabel}\\\"}}}}\""); - string node; - if (!string.IsNullOrEmpty(container.ContainerBringNodePath)) - { - node = container.ContainerBringNodePath; - } - else - { - node = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), "node10", "bin", $"node{IOUtil.ExeExtension}")); - } - container.ContainerEntryPoint = node; - container.ContainerEntryPointArgs = $"-e \"setInterval(function(){{}}, 24 * 60 * 60 * 1000);\""; + string node; + if (!string.IsNullOrEmpty(container.ContainerBringNodePath)) + { + node = container.ContainerBringNodePath; } - - 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) + else { - throw new InvalidOperationException($"Docker start fail with exit code {startExitCode}"); + node = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), "node10", "bin", $"node{IOUtil.ExeExtension}")); } + container.ContainerEntryPoint = node; + container.ContainerEntryPointArgs = $"-e \"setInterval(function(){{}}, 24 * 60 * 60 * 1000);\""; } - finally + + 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) { - // Logout for private registry - if (!string.IsNullOrEmpty(registryServer)) - { - int logoutExitCode = await _dockerManger.DockerLogout(executionContext, registryServer); - if (logoutExitCode != 0) - { - executionContext.Error($"Docker logout fail with exit code {logoutExitCode}"); - } - } + throw new InvalidOperationException($"Docker start fail with exit code {startExitCode}"); } try diff --git a/src/Runner.Worker/Handlers/StepHost.cs b/src/Runner.Worker/Handlers/StepHost.cs index b61d0a88900..dea4a37f58c 100644 --- a/src/Runner.Worker/Handlers/StepHost.cs +++ b/src/Runner.Worker/Handlers/StepHost.cs @@ -138,12 +138,6 @@ public async Task ExecuteAsync(string workingDirectory, // [OPTIONS] dockerCommandArgs.Add($"-i"); -#if !OS_WINDOWS - if (!String.IsNullOrEmpty(Container.CurrentUserName)) - { - dockerCommandArgs.Add($"-u {Container.CurrentUserId}"); - } -#endif foreach (var env in environment) { // e.g. -e MY_SECRET maps the value into the exec'ed process without exposing From 075fb70667998380ff3aa1bba4d1dae74b6ca6d8 Mon Sep 17 00:00:00 2001 From: David Kale Date: Wed, 12 Jun 2019 18:35:50 -0400 Subject: [PATCH 13/24] WIP --- src/Runner.Worker/ContainerOperationProvider.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Runner.Worker/ContainerOperationProvider.cs b/src/Runner.Worker/ContainerOperationProvider.cs index 1bdb1e1c9e9..62b028b377e 100644 --- a/src/Runner.Worker/ContainerOperationProvider.cs +++ b/src/Runner.Worker/ContainerOperationProvider.cs @@ -180,8 +180,9 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Tools), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Tools)))); #else - string workingDirectory = HostContext.GetDirectory(WellKnownDirectory.Work); - container.MountVolumes.Add(new MountVolume(container.TranslateToHostPath(workingDirectory), container.TranslateToContainerPath(workingDirectory))); + string workingDirectory = executionContext.GetRunnerContext("pipelineworkspace"); + //container.MountVolumes.Add(new MountVolume(container.TranslateToHostPath(workingDirectory), container.TranslateToContainerPath(workingDirectory))); + container.MountVolumes.Add(new MountVolume(container.TranslateToHostPath(workingDirectory), "/__w")); container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Temp), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Temp)))); container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Tools), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Tools)))); container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Actions), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Actions)))); From e4d058453c562b0577e5de7071f49b55761fcb4a Mon Sep 17 00:00:00 2001 From: David Kale Date: Thu, 13 Jun 2019 13:41:16 -0400 Subject: [PATCH 14/24] Add repo patch --- src/Runner.Worker/ExecutionContext.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Runner.Worker/ExecutionContext.cs b/src/Runner.Worker/ExecutionContext.cs index 8bf9dc78bc9..a05ce86e95c 100644 --- a/src/Runner.Worker/ExecutionContext.cs +++ b/src/Runner.Worker/ExecutionContext.cs @@ -582,7 +582,21 @@ public void InitializeJob(Pipelines.AgentJobRequestMessage message, Cancellation var base64EncodingToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"x-access-token:{githubAccessToken}")); HostContext.SecretMasker.AddValue(base64EncodingToken); } - + else + { + var selfRepo = message.Resources.Repositories.Single(x => string.Equals(x.Alias, Pipelines.PipelineConstants.SelfAlias, StringComparison.OrdinalIgnoreCase)); + if (selfRepo.Endpoint != null) + { + var repoEndpoint = message.Resources.Endpoints.FirstOrDefault(x => x.Id == selfRepo.Endpoint.Id); + if (repoEndpoint?.Authorization?.Parameters != null && repoEndpoint.Authorization.Parameters.ContainsKey("accessToken")) + { + githubAccessToken = repoEndpoint.Authorization.Parameters["accessToken"]; + githubContext["token"] = new StringContextData(githubAccessToken); + var base64EncodingToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"x-access-token:{githubAccessToken}")); + HostContext.SecretMasker.AddValue(base64EncodingToken); + } + } + } ExpressionValues["github"] = githubContext; } From 48a821462d68fa47f21ea34ebff52c1cd166d6bb Mon Sep 17 00:00:00 2001 From: David Kale Date: Fri, 14 Jun 2019 15:12:00 -0400 Subject: [PATCH 15/24] Add network to action container --- src/Runner.Worker/Container/ContainerInfo.cs | 2 -- src/Runner.Worker/Container/DockerCommandManager.cs | 4 ++-- src/Runner.Worker/ContainerOperationProvider.cs | 13 +++++-------- .../Handlers/ContainerActionHandler.cs | 5 +---- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/Runner.Worker/Container/ContainerInfo.cs b/src/Runner.Worker/Container/ContainerInfo.cs index 608a26bff43..7d42f9dde82 100644 --- a/src/Runner.Worker/Container/ContainerInfo.cs +++ b/src/Runner.Worker/Container/ContainerInfo.cs @@ -46,13 +46,11 @@ public ContainerInfo(IHostContext hostContext, Pipelines.ContainerResource conta this.ContainerWorkDirectory = "C:\\__w"; _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Work)] = this.ContainerWorkDirectory; _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Tools)] = "C:\\__t"; // Tool cache folder may come from ENV, so we need a unique folder to avoid collision - _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Root)] = "C:\\__a"; // add -v '\\.\pipe\docker_engine:\\.\pipe\docker_engine' when they are available (17.09) #else this.ContainerWorkDirectory = "/__w"; _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Work)] = this.ContainerWorkDirectory; _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Tools)] = "/__t"; // Tool cache folder may come from ENV, so we need a unique folder to avoid collision - _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Root)] = "/__a"; if (this.IsJobContainer) { this.MountVolumes.Add(new MountVolume("/var/run/docker.sock", "/var/run/docker.sock")); diff --git a/src/Runner.Worker/Container/DockerCommandManager.cs b/src/Runner.Worker/Container/DockerCommandManager.cs index 4dd88df24c9..238c0e1841b 100644 --- a/src/Runner.Worker/Container/DockerCommandManager.cs +++ b/src/Runner.Worker/Container/DockerCommandManager.cs @@ -173,7 +173,7 @@ public async Task DockerCreate(IExecutionContext context, ContainerInfo dockerOptions.Add($"{container.ContainerImage}"); // COMMAND - // Intentionally blank. Always overwrite ENTRYPOINT and send ARGs + // Intentionally blank. Always overwrite ENTRYPOINT and/or send ARGs // [ARG...] dockerOptions.Add($"{container.ContainerEntryPointArgs}"); @@ -232,7 +232,7 @@ public async Task DockerRun(IExecutionContext context, ContainerInfo contai dockerOptions.Add($"{container.ContainerImage}"); // COMMAND - // Intentionally blank. Always overwrite ENTRYPOINT and send ARGs + // Intentionally blank. Always overwrite ENTRYPOINT and/or send ARGs // [ARG...] dockerOptions.Add($"{container.ContainerEntryPointArgs}"); diff --git a/src/Runner.Worker/ContainerOperationProvider.cs b/src/Runner.Worker/ContainerOperationProvider.cs index 62b028b377e..f2792f688d8 100644 --- a/src/Runner.Worker/ContainerOperationProvider.cs +++ b/src/Runner.Worker/ContainerOperationProvider.cs @@ -25,7 +25,7 @@ public interface IContainerOperationProvider : IRunnerService public class ContainerOperationProvider : RunnerService, IContainerOperationProvider { - private const string _nodeJsPathLabel = "com.azure.dev.pipelines.agent.handler.node.path"; + private const string _nodeJsPathLabel = "github.actions.runner.node.path"; private IDockerCommandManager _dockerManger; public override void Initialize(IHostContext hostContext) @@ -173,21 +173,18 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta Trace.Info($"User provided volume: {volume.Value}"); } - // Mount folder into container + // Mount folders into container + string workingDirectory = executionContext.GetRunnerContext("pipelineworkspace"); #if OS_WINDOWS + container.MountVolumes.Add(new MountVolume(container.TranslateToHostPath(workingDirectory), "C:\\__w")); container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Externals), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals)))); - container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Work), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Work)))); - container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Tools), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Tools)))); #else - - string workingDirectory = executionContext.GetRunnerContext("pipelineworkspace"); - //container.MountVolumes.Add(new MountVolume(container.TranslateToHostPath(workingDirectory), container.TranslateToContainerPath(workingDirectory))); container.MountVolumes.Add(new MountVolume(container.TranslateToHostPath(workingDirectory), "/__w")); container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Temp), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Temp)))); - container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Tools), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Tools)))); container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Actions), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Actions)))); 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.Tools), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Tools)))); if (container.IsJobContainer) { diff --git a/src/Runner.Worker/Handlers/ContainerActionHandler.cs b/src/Runner.Worker/Handlers/ContainerActionHandler.cs index bb2d46d4911..9600f107f54 100644 --- a/src/Runner.Worker/Handlers/ContainerActionHandler.cs +++ b/src/Runner.Worker/Handlers/ContainerActionHandler.cs @@ -59,10 +59,7 @@ public async Task RunAsync() container.ContainerEntryPoint = Inputs.GetValueOrDefault("entryPoint"); container.ContainerEntryPointArgs = Inputs.GetValueOrDefault("args"); - if (ExecutionContext.Variables.TryGetValue("Agent.ContainerNetwork", out string containerNetwork)) - { - container.ContainerNetwork = containerNetwork; - } + container.ContainerNetwork = ExecutionContext.GetRunnerContext("containernetwork"); var githubContext = ExecutionContext.ExpressionValues["github"] as GitHubContext; ArgUtil.NotNull(githubContext, nameof(githubContext)); From 9ef57a49b0dc27ffd231212a7f1777351ccbaebc Mon Sep 17 00:00:00 2001 From: David Kale Date: Fri, 14 Jun 2019 15:59:17 -0400 Subject: [PATCH 16/24] Remove unused strings --- src/Misc/layoutbin/en-US/strings.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Misc/layoutbin/en-US/strings.json b/src/Misc/layoutbin/en-US/strings.json index 7683a3842c0..45f2cbe9f41 100644 --- a/src/Misc/layoutbin/en-US/strings.json +++ b/src/Misc/layoutbin/en-US/strings.json @@ -14,7 +14,6 @@ "AgentRunningBehindProxy": "Agent is running behind proxy server: '{0}'", "AgentVersion": "Current runner version: '{0}'", "AgentWithSameNameAlreadyExistInPool": "Pool {0} already contains an agent with name {1}.", - "AllowContainerUserRunDocker": "Allow user '{0}' run any docker command without SUDO.", "AlreadyConfiguredError": "Cannot configure the agent because it is already configured. To reconfigure the agent, run 'config.cmd remove' or './config.sh remove' first.", "AttachFileNotExist": "Can't attach (type:{0} name:{1}) file: {2}. File does not exist.", "AttemptRemoveCredFromConfig": "An unsuccessful attempt was made using git command line to remove \"http.extraheader\" from the git config. Attempting to modify the git config file directly to remove the credential.", @@ -172,7 +171,6 @@ "ConnectToServer": "Connecting to the server.", "ContainerWindowsVersionRequirement": "Container feature requires Windows Server 1803 or higher. Please reference documentation (https://go.microsoft.com/fwlink/?linkid=875268)", "CouldNotRemoveService": "Could not delete service '{0}'", - "CreateUserWithSameUIDInsideContainer": "Try create an user with UID '{0}' inside the container.", "DeletingCredentials": "Removing .credentials", "DeletingSettings": "Removing .agent", "DirectoryHierarchyUnauthorized": "Permission to read the directory contents is required for '{0}' and each directory up the hierarchy. {1}", @@ -194,7 +192,6 @@ "FileNotFound": "File not found: '{0}'", "FinalizeJob": "Finalize Job", "GenerateAndRunUpdateScript": "Generate and execute update script.", - "GrantContainerUserSUDOPrivilege": "Grant user '{0}' SUDO privilege and allow it run any command without authentication.", "GrantingFilePermissions": "Granting file permissions to '{0}'.", "GroupDoesNotExists": "Group: {0} does not Exist", "InitializeContainer": "Initialize containers", From 77ab924a275a47248231779da8a760284b7c3459 Mon Sep 17 00:00:00 2001 From: David Kale Date: Mon, 17 Jun 2019 11:25:18 -0400 Subject: [PATCH 17/24] Formatting --- src/Runner.Worker/Container/DockerCommandManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Runner.Worker/Container/DockerCommandManager.cs b/src/Runner.Worker/Container/DockerCommandManager.cs index 238c0e1841b..3c8b80ad5b8 100644 --- a/src/Runner.Worker/Container/DockerCommandManager.cs +++ b/src/Runner.Worker/Container/DockerCommandManager.cs @@ -119,7 +119,7 @@ public async Task DockerCreate(IExecutionContext context, ContainerInfo dockerOptions.Add($"--label {DockerInstanceLabel}"); if (!string.IsNullOrEmpty(container.ContainerWorkDirectory)) { - dockerOptions.Add($"--workdir {container.ContainerWorkDirectory}"); + dockerOptions.Add($"--workdir {container.ContainerWorkDirectory}"); } if (!string.IsNullOrEmpty(container.ContainerNetwork)) { From 7a61ca3393058398b40affe2a950a00a57628259 Mon Sep 17 00:00:00 2001 From: David Kale Date: Mon, 17 Jun 2019 12:14:42 -0400 Subject: [PATCH 18/24] Set service ports in runner context / env --- src/Runner.Worker/ContainerOperationProvider.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Runner.Worker/ContainerOperationProvider.cs b/src/Runner.Worker/ContainerOperationProvider.cs index f2792f688d8..af1edcee7dd 100644 --- a/src/Runner.Worker/ContainerOperationProvider.cs +++ b/src/Runner.Worker/ContainerOperationProvider.cs @@ -246,7 +246,9 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta container.AddPortMappings(await _dockerManger.DockerPort(executionContext, container.ContainerId)); foreach (var port in container.PortMappings) { - executionContext.SetRunnerContext($"service.{container.ContainerNetworkAlias}.ports.{port.ContainerPort}", port.HostPort); + var contextVarName = $"service_{container.ContainerNetworkAlias}_ports_{port.ContainerPort}"; + contextVarName = contextVarName.ToUpperInvariant(); + executionContext.SetRunnerContext(contextVarName, port.HostPort); } } } From 498a54b0b622620e49b4c5dab3cace6c693b562e Mon Sep 17 00:00:00 2001 From: David Kale Date: Tue, 18 Jun 2019 13:12:12 -0400 Subject: [PATCH 19/24] Test forming --workdir from base workspace dir + repository name --- src/Runner.Worker/ContainerOperationProvider.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Runner.Worker/ContainerOperationProvider.cs b/src/Runner.Worker/ContainerOperationProvider.cs index af1edcee7dd..5a98ff00158 100644 --- a/src/Runner.Worker/ContainerOperationProvider.cs +++ b/src/Runner.Worker/ContainerOperationProvider.cs @@ -13,6 +13,7 @@ using Microsoft.Win32; using GitHub.Runner.Common; using GitHub.Runner.Sdk; +using GitHub.DistributedTask.Pipelines.ContextData; namespace GitHub.Runner.Worker { @@ -174,12 +175,17 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta } // Mount folders into container - string workingDirectory = executionContext.GetRunnerContext("pipelineworkspace"); + var githubContext = executionContext.ExpressionValues["github"] as GitHubContext; + var repository = githubContext["repository"] as StringContextData; + var defaultSourceDirectory = repository.ToString().Split("/")[1]; + string workspace = executionContext.GetRunnerContext("pipelineWorkspace"); #if OS_WINDOWS - container.MountVolumes.Add(new MountVolume(container.TranslateToHostPath(workingDirectory), "C:\\__w")); + container.ContainerWorkDirectory = Path.Combine("C:\\__w", defaultSourceDirectory); + container.MountVolumes.Add(new MountVolume(container.TranslateToHostPath(workspace), "C:\\__w")); container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Externals), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals)))); #else - container.MountVolumes.Add(new MountVolume(container.TranslateToHostPath(workingDirectory), "/__w")); + container.ContainerWorkDirectory = Path.Combine("/__w", defaultSourceDirectory); + container.MountVolumes.Add(new MountVolume(container.TranslateToHostPath(workspace), "/__w")); 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.Externals), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals)), true)); From e93c085b325e788be71aa68ab1affd40a195c0f0 Mon Sep 17 00:00:00 2001 From: David Kale Date: Tue, 18 Jun 2019 15:26:47 -0400 Subject: [PATCH 20/24] Save default repository / source dir name in context Avoid needing to fumble paths together by splitting org/repo name later --- src/Runner.Worker/ContainerOperationProvider.cs | 4 +--- src/Runner.Worker/JobExtension.cs | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Runner.Worker/ContainerOperationProvider.cs b/src/Runner.Worker/ContainerOperationProvider.cs index 5a98ff00158..9b382990e67 100644 --- a/src/Runner.Worker/ContainerOperationProvider.cs +++ b/src/Runner.Worker/ContainerOperationProvider.cs @@ -175,9 +175,7 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta } // Mount folders into container - var githubContext = executionContext.ExpressionValues["github"] as GitHubContext; - var repository = githubContext["repository"] as StringContextData; - var defaultSourceDirectory = repository.ToString().Split("/")[1]; + var defaultSourceDirectory = executionContext.GetRunnerContext("defaultSourceDirectory"); string workspace = executionContext.GetRunnerContext("pipelineWorkspace"); #if OS_WINDOWS container.ContainerWorkDirectory = Path.Combine("C:\\__w", defaultSourceDirectory); diff --git a/src/Runner.Worker/JobExtension.cs b/src/Runner.Worker/JobExtension.cs index dc9dc856b4b..faea92b9a93 100644 --- a/src/Runner.Worker/JobExtension.cs +++ b/src/Runner.Worker/JobExtension.cs @@ -78,6 +78,7 @@ public async Task> InitializeJob(IExecutionContext jobContext, Pipel string _workDirectory = HostContext.GetDirectory(WellKnownDirectory.Work); context.SetRunnerContext("pipelineWorkspace", Path.Combine(_workDirectory, trackingConfig.PipelineDirectory)); + context.SetRunnerContext("defaultSourceDirectory", trackingConfig.SourcesDirectory); context.SetGitHubContext("workspace", Path.Combine(_workDirectory, trackingConfig.SourcesDirectory)); // Download actions if not already in the cache From 3916b35e28db075d9a4e382229fc7e713df45ecf Mon Sep 17 00:00:00 2001 From: David Kale Date: Tue, 18 Jun 2019 15:32:12 -0400 Subject: [PATCH 21/24] Mount entire work directory in container workdir is relative path from workdir which is concat from host work dir and source dir --- src/Runner.Worker/ContainerOperationProvider.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Runner.Worker/ContainerOperationProvider.cs b/src/Runner.Worker/ContainerOperationProvider.cs index 9b382990e67..874d3ed2399 100644 --- a/src/Runner.Worker/ContainerOperationProvider.cs +++ b/src/Runner.Worker/ContainerOperationProvider.cs @@ -176,14 +176,13 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta // Mount folders into container var defaultSourceDirectory = executionContext.GetRunnerContext("defaultSourceDirectory"); - string workspace = executionContext.GetRunnerContext("pipelineWorkspace"); #if OS_WINDOWS container.ContainerWorkDirectory = Path.Combine("C:\\__w", defaultSourceDirectory); - container.MountVolumes.Add(new MountVolume(container.TranslateToHostPath(workspace), "C:\\__w")); + container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Work), "C:\\__w")); container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Externals), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals)))); #else container.ContainerWorkDirectory = Path.Combine("/__w", defaultSourceDirectory); - container.MountVolumes.Add(new MountVolume(container.TranslateToHostPath(workspace), "/__w")); + container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Work), "/__w")); 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.Externals), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals)), true)); From 36fd6f5de8a2ba4fd94034c407c10c55af640705 Mon Sep 17 00:00:00 2001 From: David Kale Date: Tue, 18 Jun 2019 15:40:10 -0400 Subject: [PATCH 22/24] Fix --- src/Runner.Worker/Container/ContainerInfo.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Runner.Worker/Container/ContainerInfo.cs b/src/Runner.Worker/Container/ContainerInfo.cs index 7d42f9dde82..e3943d8e08b 100644 --- a/src/Runner.Worker/Container/ContainerInfo.cs +++ b/src/Runner.Worker/Container/ContainerInfo.cs @@ -43,13 +43,11 @@ public ContainerInfo(IHostContext hostContext, Pipelines.ContainerResource conta this.IsJobContainer = isJobContainer; #if OS_WINDOWS - this.ContainerWorkDirectory = "C:\\__w"; - _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Work)] = this.ContainerWorkDirectory; + _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Work)] = "C:\\__w"; _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Tools)] = "C:\\__t"; // Tool cache folder may come from ENV, so we need a unique folder to avoid collision // add -v '\\.\pipe\docker_engine:\\.\pipe\docker_engine' when they are available (17.09) #else - this.ContainerWorkDirectory = "/__w"; - _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Work)] = this.ContainerWorkDirectory; + _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Work)] = "/__w"; _pathMappings[hostContext.GetDirectory(WellKnownDirectory.Tools)] = "/__t"; // Tool cache folder may come from ENV, so we need a unique folder to avoid collision if (this.IsJobContainer) { From e5e73e1ee12f8d137a0fa1cfbcec620a695afa46 Mon Sep 17 00:00:00 2001 From: David Kale Date: Wed, 19 Jun 2019 13:19:44 -0400 Subject: [PATCH 23/24] Use TranslateToContainerPath to map working dir --- src/Runner.Worker/ContainerOperationProvider.cs | 12 +++++------- src/Runner.Worker/JobExtension.cs | 1 - 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/Runner.Worker/ContainerOperationProvider.cs b/src/Runner.Worker/ContainerOperationProvider.cs index 874d3ed2399..abdd02a4711 100644 --- a/src/Runner.Worker/ContainerOperationProvider.cs +++ b/src/Runner.Worker/ContainerOperationProvider.cs @@ -175,19 +175,17 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta } // Mount folders into container - var defaultSourceDirectory = executionContext.GetRunnerContext("defaultSourceDirectory"); + var workingDirectory = executionContext.GetRunnerContext("pipelineWorkspace"); + container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Work), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Work)))); #if OS_WINDOWS - container.ContainerWorkDirectory = Path.Combine("C:\\__w", defaultSourceDirectory); - container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Work), "C:\\__w")); container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Externals), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals)))); #else - container.ContainerWorkDirectory = Path.Combine("/__w", defaultSourceDirectory); - container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Work), "/__w")); - 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.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)))); + container.ContainerWorkDirectory = container.TranslateToContainerPath(workingDirectory); if (container.IsJobContainer) { diff --git a/src/Runner.Worker/JobExtension.cs b/src/Runner.Worker/JobExtension.cs index faea92b9a93..dc9dc856b4b 100644 --- a/src/Runner.Worker/JobExtension.cs +++ b/src/Runner.Worker/JobExtension.cs @@ -78,7 +78,6 @@ public async Task> InitializeJob(IExecutionContext jobContext, Pipel string _workDirectory = HostContext.GetDirectory(WellKnownDirectory.Work); context.SetRunnerContext("pipelineWorkspace", Path.Combine(_workDirectory, trackingConfig.PipelineDirectory)); - context.SetRunnerContext("defaultSourceDirectory", trackingConfig.SourcesDirectory); context.SetGitHubContext("workspace", Path.Combine(_workDirectory, trackingConfig.SourcesDirectory)); // Download actions if not already in the cache From e725d43ed6ebfe496875e1433f46912520b27eb9 Mon Sep 17 00:00:00 2001 From: David Kale Date: Wed, 19 Jun 2019 13:44:35 -0400 Subject: [PATCH 24/24] Fix --- src/Runner.Worker/ContainerOperationProvider.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Runner.Worker/ContainerOperationProvider.cs b/src/Runner.Worker/ContainerOperationProvider.cs index abdd02a4711..05fd97ba412 100644 --- a/src/Runner.Worker/ContainerOperationProvider.cs +++ b/src/Runner.Worker/ContainerOperationProvider.cs @@ -175,7 +175,10 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta } // Mount folders into container - var workingDirectory = executionContext.GetRunnerContext("pipelineWorkspace"); + 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))));