From 8b81b3d4707e595666243fa24622952d30df0cad Mon Sep 17 00:00:00 2001 From: David Fowler Date: Thu, 19 Mar 2020 16:11:57 -0700 Subject: [PATCH] Make user secrets work in docker scenarios. (#152) * Make user secrets work in docker scenarios. - Make build errors show up in the output * Small nits --- src/Microsoft.Tye.Core/ProcessUtil.cs | 4 -- src/Microsoft.Tye.Hosting/DockerRunner.cs | 42 +++++++++++++------ src/Microsoft.Tye.Hosting/ProcessRunner.cs | 8 ++-- .../TransformProjectsIntoContainers.cs | 13 +++--- 4 files changed, 42 insertions(+), 25 deletions(-) diff --git a/src/Microsoft.Tye.Core/ProcessUtil.cs b/src/Microsoft.Tye.Core/ProcessUtil.cs index 78f553b6..675c99dc 100644 --- a/src/Microsoft.Tye.Core/ProcessUtil.cs +++ b/src/Microsoft.Tye.Core/ProcessUtil.cs @@ -81,10 +81,6 @@ namespace Microsoft.Tye { errorDataReceived.Invoke(e.Data); } - else if (outputDataReceived != null) - { - outputDataReceived.Invoke(e.Data); - } else { errorBuilder.AppendLine(e.Data); diff --git a/src/Microsoft.Tye.Hosting/DockerRunner.cs b/src/Microsoft.Tye.Hosting/DockerRunner.cs index ff6f0265..a76ed9c0 100644 --- a/src/Microsoft.Tye.Hosting/DockerRunner.cs +++ b/src/Microsoft.Tye.Hosting/DockerRunner.cs @@ -4,11 +4,12 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.Tye.Hosting.Model; using Microsoft.Extensions.Logging; +using Microsoft.Tye.Hosting.Model; namespace Microsoft.Tye.Hosting { @@ -71,6 +72,15 @@ namespace Microsoft.Tye.Hosting var volumes = ""; var workingDirectory = docker.WorkingDirectory != null ? $"-w {docker.WorkingDirectory}" : ""; + // This is .NET specific + var userSecretStore = GetUserSecretsPathFromSecrets(); + + if (!string.IsNullOrEmpty(userSecretStore)) + { + // Map the user secrets on this drive to user secrets + docker.VolumeMappings[userSecretStore] = "/root/.microsoft/usersecrets:ro"; + } + var dockerInfo = new DockerInformation(new Task[service.Description.Replicas]); async Task RunDockerContainer(IEnumerable<(int Port, int? InternalPort, int BindingPort, string? Protocol)> ports) @@ -260,21 +270,29 @@ namespace Microsoft.Tye.Hosting } } - private static async Task DetectDockerInstalled() + private static string? GetUserSecretsPathFromSecrets() { - // Detect Docker installation - try - { - await ProcessUtil.RunAsync("docker", "version", throwOnError: false); - return true; - } - catch (Exception) + // This is the logic used to determine the user secrets path + // See https://github.com/dotnet/extensions/blob/64140f90157fec1bfd8aeafdffe8f30308ccdf41/src/Configuration/Config.UserSecrets/src/PathHelper.cs#L27 + const string userSecretsFallbackDir = "DOTNET_USER_SECRETS_FALLBACK_DIR"; + + // For backwards compat, this checks env vars first before using Env.GetFolderPath + var appData = Environment.GetEnvironmentVariable("APPDATA"); + var root = appData // On Windows it goes to %APPDATA%\Microsoft\UserSecrets\ + ?? Environment.GetEnvironmentVariable("HOME") // On Mac/Linux it goes to ~/.microsoft/usersecrets/ + ?? Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + ?? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + ?? Environment.GetEnvironmentVariable(userSecretsFallbackDir); // this fallback is an escape hatch if everything else fails + + if (string.IsNullOrEmpty(root)) { - // Unfortunately, process throws - return false; + return null; } - } + return !string.IsNullOrEmpty(appData) + ? Path.Combine(root, "Microsoft", "UserSecrets") + : Path.Combine(root, ".microsoft", "usersecrets"); + } private class DockerInformation { diff --git a/src/Microsoft.Tye.Hosting/ProcessRunner.cs b/src/Microsoft.Tye.Hosting/ProcessRunner.cs index 5d32e596..48c189ed 100644 --- a/src/Microsoft.Tye.Hosting/ProcessRunner.cs +++ b/src/Microsoft.Tye.Hosting/ProcessRunner.cs @@ -108,13 +108,13 @@ namespace Microsoft.Tye.Hosting service.Logs.OnNext($"dotnet build \"{service.Status.ProjectFilePath}\" /nologo"); - var buildResult = await ProcessUtil.RunAsync("dotnet", $"build \"{service.Status.ProjectFilePath}\" /nologo", - outputDataReceived: data => service.Logs.OnNext(data), - throwOnError: false); + var buildResult = await ProcessUtil.RunAsync("dotnet", $"build \"{service.Status.ProjectFilePath}\" /nologo", throwOnError: false); + + service.Logs.OnNext(buildResult.StandardOutput); if (buildResult.ExitCode != 0) { - _logger.LogInformation("Building {ProjectFile} failed with exit code {ExitCode}: " + buildResult.StandardOutput + buildResult.StandardError, service.Status.ProjectFilePath, buildResult.ExitCode); + _logger.LogInformation("Building {ProjectFile} failed with exit code {ExitCode}: \r\n" + buildResult.StandardOutput, service.Status.ProjectFilePath, buildResult.ExitCode); return; } } diff --git a/src/Microsoft.Tye.Hosting/TransformProjectsIntoContainers.cs b/src/Microsoft.Tye.Hosting/TransformProjectsIntoContainers.cs index 6130e993..1b44c778 100644 --- a/src/Microsoft.Tye.Hosting/TransformProjectsIntoContainers.cs +++ b/src/Microsoft.Tye.Hosting/TransformProjectsIntoContainers.cs @@ -50,13 +50,16 @@ namespace Microsoft.Tye.Hosting service.Logs.OnNext($"dotnet publish \"{service.Status.ProjectFilePath}\" /nologo"); - var buildResult = await ProcessUtil.RunAsync("dotnet", $"publish \"{service.Status.ProjectFilePath}\" /nologo", - outputDataReceived: data => service.Logs.OnNext(data), - throwOnError: false); + var buildResult = await ProcessUtil.RunAsync("dotnet", $"publish \"{service.Status.ProjectFilePath}\" /nologo", throwOnError: false); + + service.Logs.OnNext(buildResult.StandardOutput); if (buildResult.ExitCode != 0) { - _logger.LogInformation("Publishing {ProjectFile} failed with exit code {ExitCode}: " + buildResult.StandardOutput + buildResult.StandardError, service.Status.ProjectFilePath, buildResult.ExitCode); + _logger.LogInformation("Publishing {ProjectFile} failed with exit code {ExitCode}: \r\n" + buildResult.StandardOutput, service.Status.ProjectFilePath, buildResult.ExitCode); + + // Null out the RunInfo so that + serviceDescription.RunInfo = null; return; } @@ -78,7 +81,7 @@ namespace Microsoft.Tye.Hosting private static string DetermineContainerImage(string targetFramework) { - // TODO: Determine the base iamge from the tfm + // TODO: Determine the base image from the tfm return "mcr.microsoft.com/dotnet/core/sdk:3.1-buster"; }