From 68b618767d0ff652b4f3927954c3545fc0acd364 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Fri, 17 Apr 2020 11:21:05 -0700 Subject: [PATCH] Support dapr log level and config for local run Fixes #296 Adds conditional logic for passing these properties to `daprd`. Corrects logic for dealing with config. Dapr's config annotation in k8s refers to kubernetes resource by name, but that's not possible with local run. In local run `dapr` will expect the `-config` parameter to be a file on disk. So, this change adds logic to look for a file by naming convention and pass that locally instead. Additionally added the ability for an extension to output to the console - this is needed because we want to tell the user if their config file can't be found, and specifically where we looked. I made the decision to log a message and skip if the config file isn't present, because we don't have a way to make settings conditional based on environment yet - it makes sense that someone might need to have a config in production but not in local dev. --- samples/dapr/tye.yaml | 10 ++++++ src/Microsoft.Tye.Core/ExtensionContext.cs | 5 ++- .../Dapr/DaprExtension.cs | 32 ++++++++++++++++--- src/tye/ApplicationBuilderExtensions.cs | 4 +-- src/tye/BuildHost.cs | 2 +- src/tye/GenerateHost.cs | 2 +- src/tye/Program.DeployCommand.cs | 2 +- src/tye/Program.PushCommand.cs | 2 +- src/tye/Program.RunCommand.cs | 2 +- 9 files changed, 48 insertions(+), 13 deletions(-) diff --git a/samples/dapr/tye.yaml b/samples/dapr/tye.yaml index ca72ff09e..8d4dc7168 100644 --- a/samples/dapr/tye.yaml +++ b/samples/dapr/tye.yaml @@ -7,6 +7,16 @@ name: dapr extensions: - name: dapr + + # log-level configures the log level of the dapr sidecar + log-level: debug + + # config allows you to pass additional configuration into the dapr sidecar + # config will be interpreted as a named k8s resource when deployed, and will be interpreted as + # a file on disk when running locally at `./components/myconfig.yaml` + # + # config: myconfig + services: - name: orders project: orders/orders.csproj diff --git a/src/Microsoft.Tye.Core/ExtensionContext.cs b/src/Microsoft.Tye.Core/ExtensionContext.cs index 76ae6953e..42ca7b759 100644 --- a/src/Microsoft.Tye.Core/ExtensionContext.cs +++ b/src/Microsoft.Tye.Core/ExtensionContext.cs @@ -6,14 +6,17 @@ namespace Microsoft.Tye { public sealed class ExtensionContext { - public ExtensionContext(ApplicationBuilder application, OperationKind operation) + public ExtensionContext(ApplicationBuilder application, OutputContext output, OperationKind operation) { Application = application; + Output = output; Operation = operation; } public ApplicationBuilder Application { get; } + public OutputContext Output { get; } + public OperationKind Operation { get; } public enum OperationKind diff --git a/src/Microsoft.Tye.Extensions/Dapr/DaprExtension.cs b/src/Microsoft.Tye.Extensions/Dapr/DaprExtension.cs index 87e4456ff..5ca07cf5d 100644 --- a/src/Microsoft.Tye.Extensions/Dapr/DaprExtension.cs +++ b/src/Microsoft.Tye.Extensions/Dapr/DaprExtension.cs @@ -4,6 +4,7 @@ using System; using System.Globalization; +using System.IO; using System.Linq; using System.Threading.Tasks; @@ -49,8 +50,29 @@ public override Task ProcessAsync(ExtensionContext context, ExtensionConfigurati // These environment variables are replaced with environment variables // defined for this service. - Args = $"-app-id {project.Name} -app-port %APP_PORT% -dapr-grpc-port %DAPR_GRPC_PORT% --dapr-http-port %DAPR_HTTP_PORT% --metrics-port %METRICS_PORT% -log-level %LOG_LEVEL% -config %CONFIG% --placement-address localhost:50005", + Args = $"-app-id {project.Name} -app-port %APP_PORT% -dapr-grpc-port %DAPR_GRPC_PORT% --dapr-http-port %DAPR_HTTP_PORT% --metrics-port %METRICS_PORT% --placement-address localhost:50005", }; + + // When running locally `-config` specifies a filename, not a configuration name. By convention + // we'll assume the filename and config name are the same. + if (config.Data.TryGetValue("config", out var obj) && obj?.ToString() is string daprConfig) + { + var configFile = Path.Combine(context.Application.Source.DirectoryName, "components", $"{daprConfig}.yaml"); + if (File.Exists(configFile)) + { + proxy.Args += $" -config \"{configFile}\""; + } + else + { + context.Output.WriteAlwaysLine($"Could not find dapr config '{configFile}'. Skipping..."); + } + } + + if (config.Data.TryGetValue("log-level", out obj) && obj?.ToString() is string logLevel) + { + proxy.Args += $" -log-level {logLevel}"; + } + context.Application.Services.Add(proxy); // Listen for grpc on an auto-assigned port @@ -147,14 +169,14 @@ public override Task ProcessAsync(ExtensionContext context, ExtensionConfigurati deployment.Annotations.Add("dapr.io/id", project.Name); deployment.Annotations.Add("dapr.io/port", (httpBinding.Port ?? 80).ToString(CultureInfo.InvariantCulture)); - if (config.Data.TryGetValue("config", out var daprConfig) && daprConfig is object) + if (config.Data.TryGetValue("config", out var obj) && obj?.ToString() is string daprConfig) { - deployment.Annotations.TryAdd("dapr.io/config", daprConfig!.ToString() ?? string.Empty); + deployment.Annotations.TryAdd("dapr.io/config", daprConfig); } - if (config.Data.TryGetValue("log-level", out var logLevel) && logLevel is object) + if (config.Data.TryGetValue("log-level", out obj) && obj?.ToString() is string logLevel) { - deployment.Annotations.TryAdd("dapr.io/log-level", logLevel!.ToString() ?? string.Empty); + deployment.Annotations.TryAdd("dapr.io/log-level", logLevel); } } } diff --git a/src/tye/ApplicationBuilderExtensions.cs b/src/tye/ApplicationBuilderExtensions.cs index 5c5c67481..8ac4f4714 100644 --- a/src/tye/ApplicationBuilderExtensions.cs +++ b/src/tye/ApplicationBuilderExtensions.cs @@ -14,7 +14,7 @@ public static class ApplicationBuilderExtensions { // For layering reasons this has to live in the `tye` project. We don't want to leak // the extensions themselves into Tye.Core. - public static async Task ProcessExtensionsAsync(this ApplicationBuilder application, ExtensionContext.OperationKind operation) + public static async Task ProcessExtensionsAsync(this ApplicationBuilder application, OutputContext output, ExtensionContext.OperationKind operation) { foreach (var extensionConfig in application.Extensions) { @@ -23,7 +23,7 @@ public static async Task ProcessExtensionsAsync(this ApplicationBuilder applicat throw new CommandException($"Could not find the extension '{extensionConfig.Name}'."); } - var context = new ExtensionContext(application, operation); + var context = new ExtensionContext(application, output, operation); await extension.ProcessAsync(context, extensionConfig); } } diff --git a/src/tye/BuildHost.cs b/src/tye/BuildHost.cs index 9b234cf81..03a81b011 100644 --- a/src/tye/BuildHost.cs +++ b/src/tye/BuildHost.cs @@ -25,7 +25,7 @@ public static async Task BuildAsync(IConsole console, FileInfo path, Verbosity v public static async Task ExecuteBuildAsync(OutputContext output, ApplicationBuilder application, string environment, bool interactive) { - await application.ProcessExtensionsAsync(ExtensionContext.OperationKind.Deploy); + await application.ProcessExtensionsAsync(output, ExtensionContext.OperationKind.Deploy); var steps = new List() { diff --git a/src/tye/GenerateHost.cs b/src/tye/GenerateHost.cs index a3b255608..b6d85acf9 100644 --- a/src/tye/GenerateHost.cs +++ b/src/tye/GenerateHost.cs @@ -28,7 +28,7 @@ public static async Task GenerateAsync(IConsole console, FileInfo path, Verbosit public static async Task ExecuteGenerateAsync(OutputContext output, ApplicationBuilder application, string environment, bool interactive) { - await application.ProcessExtensionsAsync(ExtensionContext.OperationKind.Deploy); + await application.ProcessExtensionsAsync(output, ExtensionContext.OperationKind.Deploy); var steps = new List() { diff --git a/src/tye/Program.DeployCommand.cs b/src/tye/Program.DeployCommand.cs index 0f76e87e6..9576ceeea 100644 --- a/src/tye/Program.DeployCommand.cs +++ b/src/tye/Program.DeployCommand.cs @@ -64,7 +64,7 @@ private static async Task ExecuteDeployAsync(OutputContext output, ApplicationBu throw new CommandException($"Cannot apply manifests because kubectl is not connected to a cluster."); } - await application.ProcessExtensionsAsync(ExtensionContext.OperationKind.Deploy); + await application.ProcessExtensionsAsync(output, ExtensionContext.OperationKind.Deploy); var steps = new List() { diff --git a/src/tye/Program.PushCommand.cs b/src/tye/Program.PushCommand.cs index 5519a6e70..91e348484 100644 --- a/src/tye/Program.PushCommand.cs +++ b/src/tye/Program.PushCommand.cs @@ -54,7 +54,7 @@ public static Command CreatePushCommand() private static async Task ExecutePushAsync(OutputContext output, ApplicationBuilder application, string environment, bool interactive, bool force) { - await application.ProcessExtensionsAsync(ExtensionContext.OperationKind.Deploy); + await application.ProcessExtensionsAsync(output, ExtensionContext.OperationKind.Deploy); var steps = new List() { diff --git a/src/tye/Program.RunCommand.cs b/src/tye/Program.RunCommand.cs index 3739ebd98..c3abef4f0 100644 --- a/src/tye/Program.RunCommand.cs +++ b/src/tye/Program.RunCommand.cs @@ -87,7 +87,7 @@ private static Command CreateRunCommand(string[] args) throw new CommandException($"No services found in \"{application.Source.Name}\""); } - await application.ProcessExtensionsAsync(ExtensionContext.OperationKind.LocalRun); + await application.ProcessExtensionsAsync(output, ExtensionContext.OperationKind.LocalRun); InitializeThreadPoolSettings(application.Services.Count);