From b256a12bfcb8a58ba55b8d7f8ad9abcff4c26b24 Mon Sep 17 00:00:00 2001 From: Phill Morton Date: Thu, 28 Sep 2023 02:54:42 +0100 Subject: [PATCH] add environment variable token replacement --- .../Model/Application.cs | 3 +- src/Microsoft.Tye.Hosting/TokenReplacement.cs | 64 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.Tye.Hosting/Model/Application.cs b/src/Microsoft.Tye.Hosting/Model/Application.cs index 49453aee..47e1eeb6 100644 --- a/src/Microsoft.Tye.Hosting/Model/Application.cs +++ b/src/Microsoft.Tye.Hosting/Model/Application.cs @@ -50,9 +50,10 @@ namespace Microsoft.Tye.Hosting.Model // Inject normal configuration foreach (var pair in service.Description.Configuration) { + if (pair.Value is object) { - set(pair.Name, pair.Value); + set(pair.Name, TokenReplacement.ReplaceEnvironmentValues(pair.Value, bindings)); } else if (pair.Source is object) { diff --git a/src/Microsoft.Tye.Hosting/TokenReplacement.cs b/src/Microsoft.Tye.Hosting/TokenReplacement.cs index 97c78864..94c204be 100644 --- a/src/Microsoft.Tye.Hosting/TokenReplacement.cs +++ b/src/Microsoft.Tye.Hosting/TokenReplacement.cs @@ -12,6 +12,29 @@ namespace Microsoft.Tye.Hosting { internal static class TokenReplacement { + public static string ReplaceEnvironmentValues(string text, List bindings) + { + var tokens = GetTokens(text); + foreach (var token in tokens) + { + var selectedBinding = ResolveEnvironmentToken(token, bindings); + if (selectedBinding is null) + { + throw new InvalidOperationException($"No available substitutions found for token '{token}'."); + } + + text = selectedBinding.Value.Item2; + var selectedBindingtokens = GetTokens(selectedBinding.Value.Item2); + foreach (var bindingtoken in selectedBindingtokens) + { + var replacement = ReplaceValues(bindingtoken,selectedBinding.Value.Item1, bindings); + text = text.Replace(bindingtoken, replacement); + } + + } + + return text; + } public static string ReplaceValues(string text, EffectiveBinding binding, List bindings) { var tokens = GetTokens(text); @@ -58,6 +81,47 @@ namespace Microsoft.Tye.Hosting return tokens; } + + private static (EffectiveBinding, string?)? ResolveEnvironmentToken(string token, List bindings) + { + // The language we support for tokens is meant to be pretty DRY. It supports a few different formats: + // + // - ${host}: only allowed inside a connection string, it can refer to the binding. + // - ${env:SOME_VAR}: allowed anywhere. It can refer to any environment variable defined for *this* service. + // - ${service:myservice:port}: allowed anywhere. It can refer to the protocol/host/port of bindings. + + var keys = token[2..^1].Split(':'); + if (keys.Length == 3 && keys[0] == "service") + { + var binding = bindings.FirstOrDefault(b => b.Service == keys[1] && b.Name == null)!; + return (binding, GetValueFromBinding(binding, keys[2])); + } + else if (keys.Length == 4 && keys[0] == "service") + { + var binding = bindings.FirstOrDefault(b => b.Service == keys[1] && b.Name == keys[2])!; + return (binding, GetValueFromBinding(binding, keys[3])); + } + + return null; + + string? GetValueFromBinding(EffectiveBinding binding, string key) + { + return key switch + { + "protocol" => binding.Protocol, + "host" => binding.Host, + "port" => binding.Port?.ToString(CultureInfo.InvariantCulture), + "connectionString" => binding.ConnectionString, + _ => null, + }; + } + + static string? GetEnvironmentVariable(EffectiveBinding binding, string key) + { + var envVar = binding.Env.FirstOrDefault(e => string.Equals(e.Name, key, StringComparison.OrdinalIgnoreCase)); + return envVar?.Value; + } + } private static string? ResolveToken(string token, EffectiveBinding binding, List bindings) {