From 97a8c5d75af72d3d1fad43f183c1306a67f74b38 Mon Sep 17 00:00:00 2001 From: "jaine.ch" Date: Tue, 1 Dec 2020 13:53:42 +0800 Subject: [PATCH 1/3] package as nuget --- .dockerignore | 20 -- .gitignore | 1 + ConfigMapFileProviderSample.sln | 25 -- LICENSE | 21 -- README.md | 206 ----------- media/article-preview.png | Bin 62897 -> 0 bytes .../ConfigMapFileProvider.cs | 155 +++++---- .../ConfigMapFileProvider.csproj | 23 ++ .../ConfigMapFileProviderChangeToken.cs | 328 +++++++++--------- .../ConfigMapFileProviderSample.csproj | 16 - .../Controllers/ValuesController.cs | 58 ---- src/ConfigMapFileProviderSample/Dockerfile | 19 - src/ConfigMapFileProviderSample/Program.cs | 33 -- src/ConfigMapFileProviderSample/Startup.cs | 42 --- .../appsettings.Development.json | 9 - .../appsettings.json | 8 - .../configmap.yaml | 15 - .../deployment.yaml | 29 -- 18 files changed, 267 insertions(+), 741 deletions(-) delete mode 100644 .dockerignore delete mode 100644 ConfigMapFileProviderSample.sln delete mode 100644 LICENSE delete mode 100644 README.md delete mode 100644 media/article-preview.png rename src/{ConfigMapFileProviderSample => ConfigMapFileProvider}/ConfigMapFileProvider.cs (89%) create mode 100644 src/ConfigMapFileProvider/ConfigMapFileProvider.csproj rename src/{ConfigMapFileProviderSample => ConfigMapFileProvider}/ConfigMapFileProviderChangeToken.cs (95%) delete mode 100644 src/ConfigMapFileProviderSample/ConfigMapFileProviderSample.csproj delete mode 100644 src/ConfigMapFileProviderSample/Controllers/ValuesController.cs delete mode 100644 src/ConfigMapFileProviderSample/Dockerfile delete mode 100644 src/ConfigMapFileProviderSample/Program.cs delete mode 100644 src/ConfigMapFileProviderSample/Startup.cs delete mode 100644 src/ConfigMapFileProviderSample/appsettings.Development.json delete mode 100644 src/ConfigMapFileProviderSample/appsettings.json delete mode 100644 src/ConfigMapFileProviderSample/configmap.yaml delete mode 100644 src/ConfigMapFileProviderSample/deployment.yaml diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index e52404d..0000000 --- a/.dockerignore +++ /dev/null @@ -1,20 +0,0 @@ -**/.dockerignore -**/.env -**/.git -**/.gitignore -**/.vs -**/.vscode -**/*.*proj.user -**/azds.yaml -**/charts -**/bin -**/obj -**/Dockerfile -**/Dockerfile.develop -**/docker-compose.yml -**/docker-compose.*.yml -**/*.dbmdl -**/*.jfm -**/secrets.dev.yaml -**/values.dev.yaml -**/.toolstarget \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3e759b7..d7bec8d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ *.user *.userosscache *.sln.docstates +*.bat # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs diff --git a/ConfigMapFileProviderSample.sln b/ConfigMapFileProviderSample.sln deleted file mode 100644 index 5b6ca43..0000000 --- a/ConfigMapFileProviderSample.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29215.179 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConfigMapFileProviderSample", "src\ConfigMapFileProviderSample\ConfigMapFileProviderSample.csproj", "{1343A1BF-33E0-49D5-AADB-A323867697AE}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1343A1BF-33E0-49D5-AADB-A323867697AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1343A1BF-33E0-49D5-AADB-A323867697AE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1343A1BF-33E0-49D5-AADB-A323867697AE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1343A1BF-33E0-49D5-AADB-A323867697AE}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {AB82D515-15E8-4AAE-A7B7-0CDC78DB7234} - EndGlobalSection -EndGlobal diff --git a/LICENSE b/LICENSE deleted file mode 100644 index ef66d83..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 Francisco Beltrao - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index 1faa50b..0000000 --- a/README.md +++ /dev/null @@ -1,206 +0,0 @@ -# .NET Configuration in Kubernetes config maps with auto reload - -![Log level configuration in config map](media/article-preview.png) - -Kubernetes config maps allows the injection of configuration into an application. The contents of a config map can be injected as environment variables or mounted files. - -For instance, imagine you want to configure the log level in a separated file that will be mounted into your application. - -The following config map limits the verbosity to errors: - -```yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: demo-config -data: - appsettings.json: |- - { - "Logging": { - "LogLevel": { - "Default": "Error", - "System": "Error", - "Microsoft": "Error" - } - } - } -``` - -The file below deploys an application, mounting the contents of the config map into the /app/config folder. - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: demo-deployment - labels: - app: config-demo-app -spec: - replicas: 1 - selector: - matchLabels: - app: config-demo-app - template: - metadata: - labels: - app: config-demo-app - spec: - containers: - - name: configmapfileprovidersample - image: fbeltrao/configmapfileprovidersample:1.0 - ports: - - containerPort: 80 - volumeMounts: - - name: config-volume - mountPath: /app/config - volumes: - - name: config-volume - configMap: - name: demo-config -``` - -In order to read configurations from the provided path (`config/appsettings.json`) the following code changes are required: - -```c# -public static IWebHostBuilder CreateWebHostBuilder(string[] args) => - WebHost.CreateDefaultBuilder(args) - .ConfigureAppConfiguration(c => - { - c.AddJsonFile("config/appsettings.json", optional: true, reloadOnChange: true); - }) - .UseStartup(); -``` - - -Deploy the application: -```bash -kubectl apply -f configmap.yaml -kubectl apply -f deployment.yaml -``` - -We can peek into the running pod in Kubernetes, looking at the files stored in the container: - -```bash -kubectl exec -it -- bash -root@demo-deployment-844f6c6546-x786b:/app# cd config/ -root@demo-deployment-844f6c6546-x786b:/app/config# ls -la - -rwxrwxrwx 3 root root 4096 Sep 14 09:01 . -drwxr-xr-x 1 root root 4096 Sep 14 08:47 .. -drwxr-xr-x 2 root root 4096 Sep 14 09:01 ..2019_09_14_09_01_16.386067924 -lrwxrwxrwx 1 root root 31 Sep 14 09:01 ..data -> ..2019_09_14_09_01_16.386067924 -lrwxrwxrwx 1 root root 53 Sep 14 08:47 appsettings.json -> ..data/appsettings.json -``` - -As you can see, the config map content is mounted using a [symlink](https://en.wikipedia.org/wiki/Symbolic_link). - -Let's change the log verbosity to `debug`, making the following changes to the config map: - -```yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: demo-config -data: - appsettings.json: |- - { - "Logging": { - "LogLevel": { - "Default": "Debug", - "System": "Error", - "Microsoft": "Error" - } - } - } -``` -and redeploying it - -```bash -kubectl apply -f configmap.yaml -``` - -Eventually the changes will be applied to the mounted file inside the container, as you can see below: - -```bash -root@demo-deployment-844f6c6546-gzc6j:/app/config# ls -la -total 12 -drwxrwxrwx 3 root root 4096 Sep 14 09:05 . -drwxr-xr-x 1 root root 4096 Sep 14 08:47 .. -drwxr-xr-x 2 root root 4096 Sep 14 09:05 ..2019_09_14_09_05_02.797339427 -lrwxrwxrwx 1 root root 31 Sep 14 09:05 ..data -> ..2019_09_14_09_05_02.797339427 -lrwxrwxrwx 1 root root 53 Sep 14 08:47 appsettings.json -> ..data/appsettings.json -``` - -Notice that the appsettings.json last modified date does not change, only the referenced file actually gets updated. - -Unfortunately, the build-in reload on changes in .NET core file provider does not work. The config map does not trigger the configuration reload as one would expect. - -Based on my investigation, it seems that the .NET core change discovery relies on the file last modified date. Since the file we are monitoring did not change (the symlink reference did), no changes are detected. - -## Working on a solution - -This problem is tracked [here](https://github.com/aspnet/Extensions/issues/1175). Until a fix is available we can take advantage of the extensible configuration system in .NET Core and implement a file based configuration provider that detect changes based on file contents. - -The setup looks like this: -```c# -public static IWebHostBuilder CreateWebHostBuilder(string[] args) => - WebHost.CreateDefaultBuilder(args) - .ConfigureAppConfiguration(c => - { - c.AddJsonFile(ConfigMapFileProvider.FromRelativePath("config"), - "appsettings.json", - optional: true, - reloadOnChange: true); - }) - .UseStartup(); -``` - -The provided implementation detect changes based on the hash of the content. Check the sample project files for more details. - -Disclaimer: this is a quick implementation, not tested in different environments/configurations. Use at your own risk. - -### Testing the sample application - -Clone this repository then deploy the application: -```bash -kubectl apply -f configmap.yaml -kubectl apply -f deployment.yaml -``` - -In a separated console window stream the container log: -```bash -kubectl logs -l app=config-demo-app -f -``` - -Open a tunnel to the application with kubectl port-forward -```bash - kubectl port-forward 60000:80 -``` - -Verify that the log is in error level, by opening a browser and navigating to `http://localhost:60000/api/values`. Look at the pod logs. You should see the following lines: -```log -fail: ConfigMapFileProviderSample.Controllers.ValuesController[0] - ERR log -crit: ConfigMapFileProviderSample.Controllers.ValuesController[0] - CRI log -``` - -Change the config map: -Replace `"Default": "Error"` to `"Default": "Debug"` in the configmap.yaml file, then redeploy the config map. -```bash -kubectl apply -f configmap.yaml -``` - -Verify that the log level changes to Debug (it can take a couple of minutes until the file change is detected) by issuing new requests to `http://localhost:60000/api/values`. The logs will change to this: -```log -dbug: ConfigMapFileProviderSample.Controllers.ValuesController[0] - DBG log -info: ConfigMapFileProviderSample.Controllers.ValuesController[0] - INF log -warn: ConfigMapFileProviderSample.Controllers.ValuesController[0] - WRN log -fail: ConfigMapFileProviderSample.Controllers.ValuesController[0] - ERR log -crit: ConfigMapFileProviderSample.Controllers.ValuesController[0] - CRI log -``` \ No newline at end of file diff --git a/media/article-preview.png b/media/article-preview.png deleted file mode 100644 index 1e3a0cd41da5960bc4f09a3dd452f49f79f7727d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62897 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

>Y%AS1QsdT&1Nr`?tbp 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 - /// Simple implementation using config maps as source - /// Config maps volumes in Linux/Kubernetes are implemented as symlink files. - /// Once reloaded their Last modified date does not change. This implementation uses a check sum to verify - /// - public class ConfigMapFileProvider : IFileProvider - { - ConcurrentDictionary watchers; - - public static IFileProvider FromRelativePath(string subPath) - { - var executableLocation = Assembly.GetEntryAssembly().Location; - var executablePath = Path.GetDirectoryName(executableLocation); - var configPath = Path.Combine(executablePath, subPath); - if (Directory.Exists(configPath)) - { - return new ConfigMapFileProvider(configPath); - } - - return null; - } - - public ConfigMapFileProvider(string rootPath) - { - if (string.IsNullOrWhiteSpace(rootPath)) - { - throw new System.ArgumentException("Invalid root path", nameof(rootPath)); - } - - RootPath = rootPath; - watchers = new ConcurrentDictionary(); - } - - public string RootPath { get; } - - public IDirectoryContents GetDirectoryContents(string subpath) - { - return new PhysicalDirectoryContents(Path.Combine(RootPath, subpath)); - } - - public IFileInfo GetFileInfo(string subpath) - { - var fi = new FileInfo(Path.Combine(RootPath, subpath)); - return new PhysicalFileInfo(fi); - } - - public IChangeToken Watch(string filter) - { - var watcher = watchers.AddOrUpdate(filter, - addValueFactory: (f) => - { - return new ConfigMapFileProviderChangeToken(RootPath, filter); - }, - updateValueFactory: (f, e) => - { - e.Dispose(); - return new ConfigMapFileProviderChangeToken(RootPath, filter); - }); - - watcher.EnsureStarted(); - return watcher; - } - } -} +using Microsoft.Extensions.Primitives; +using System; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.IO; +using System.Reflection; +using System.Security.Cryptography; +using System.Threading; +using Timer = System.Threading.Timer; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.FileProviders.Physical; +using Microsoft.Extensions.FileProviders.Internal; + +namespace Microsoft.Extensions.Configuration +{ + ///

+ /// Simple implementation using config maps as source + /// Config maps volumes in Linux/Kubernetes are implemented as symlink files. + /// Once reloaded their Last modified date does not change. This implementation uses a check sum to verify + /// + public class ConfigMapFileProvider : IFileProvider + { + ConcurrentDictionary watchers; + + public static IFileProvider FromRelativePath(string subPath) + { + var executableLocation = Assembly.GetEntryAssembly().Location; + var executablePath = Path.GetDirectoryName(executableLocation); + var configPath = Path.Combine(executablePath, subPath); + if (Directory.Exists(configPath)) + { + return new ConfigMapFileProvider(configPath); + } + + return null; + } + + public ConfigMapFileProvider(string rootPath) + { + if (string.IsNullOrWhiteSpace(rootPath)) + { + throw new System.ArgumentException("Invalid root path", nameof(rootPath)); + } + + RootPath = rootPath; + watchers = new ConcurrentDictionary(); + } + + public string RootPath { get; } + + public IDirectoryContents GetDirectoryContents(string subpath) + { + return new PhysicalDirectoryContents(Path.Combine(RootPath, subpath)); + } + + public IFileInfo GetFileInfo(string subpath) + { + var fi = new FileInfo(Path.Combine(RootPath, subpath)); + return new PhysicalFileInfo(fi); + } + + public IChangeToken Watch(string filter) + { + var watcher = watchers.AddOrUpdate(filter, + addValueFactory: (f) => + { + return new ConfigMapFileProviderChangeToken(RootPath, filter); + }, + updateValueFactory: (f, e) => + { + e.Dispose(); + return new ConfigMapFileProviderChangeToken(RootPath, filter); + }); + + watcher.EnsureStarted(); + return watcher; + } + } +} diff --git a/src/ConfigMapFileProvider/ConfigMapFileProvider.csproj b/src/ConfigMapFileProvider/ConfigMapFileProvider.csproj new file mode 100644 index 0000000..939a0ac --- /dev/null +++ b/src/ConfigMapFileProvider/ConfigMapFileProvider.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + Dynamic reload config from file for Kubernetes with Config Map. + Usage: + configBuilder.SetBasePath(Path.Join(AppContext.BaseDirectory, "config")) + .AddJsonFile(ConfigMapFileProvider.FromRelativePath("config"), "appsettings.json", true, true); + + 1.0.0 + Jaine.ch + Jaine.ch + First version + + + + + + + + + diff --git a/src/ConfigMapFileProviderSample/ConfigMapFileProviderChangeToken.cs b/src/ConfigMapFileProvider/ConfigMapFileProviderChangeToken.cs similarity index 95% rename from src/ConfigMapFileProviderSample/ConfigMapFileProviderChangeToken.cs rename to src/ConfigMapFileProvider/ConfigMapFileProviderChangeToken.cs index 3bd70c6..d11286d 100644 --- a/src/ConfigMapFileProviderSample/ConfigMapFileProviderChangeToken.cs +++ b/src/ConfigMapFileProvider/ConfigMapFileProviderChangeToken.cs @@ -1,164 +1,164 @@ -using Microsoft.Extensions.Primitives; -using System; -using System.Collections.Generic; -using System.IO; -using System.Security.Cryptography; -using System.Threading; -using Timer = System.Threading.Timer; - -namespace ConfigMapFileProviderSample -{ - public sealed class ConfigMapFileProviderChangeToken : IChangeToken, IDisposable - { - class CallbackRegistration : IDisposable - { - Action callback; - object state; - Action unregister; - - - public CallbackRegistration(Action callback, object state, Action unregister) - { - this.callback = callback; - this.state = state; - this.unregister = unregister; - } - - public void Notify() - { - var localState = this.state; - var localCallback = this.callback; - if (localCallback != null) - { - localCallback.Invoke(localState); - } - } - - - public void Dispose() - { - var localUnregister = Interlocked.Exchange(ref unregister, null); - if (localUnregister != null) - { - localUnregister(this); - this.callback = null; - this.state = null; - } - } - } - - List registeredCallbacks; - private readonly string rootPath; - private string filter; - private readonly int detectChangeIntervalMs; - private Timer timer; - private bool hasChanged; - private string lastChecksum; - object timerLock = new object(); - - public ConfigMapFileProviderChangeToken(string rootPath, string filter, int detectChangeIntervalMs = 30_000) - { - Console.WriteLine($"new {nameof(ConfigMapFileProviderChangeToken)} for {filter}"); - registeredCallbacks = new List(); - this.rootPath = rootPath; - this.filter = filter; - this.detectChangeIntervalMs = detectChangeIntervalMs; - } - - internal void EnsureStarted() - { - lock (timerLock) - { - if (timer == null) - { - var fullPath = Path.Combine(rootPath, filter); - if (File.Exists(fullPath)) - { - this.timer = new Timer(CheckForChanges); - this.timer.Change(0, detectChangeIntervalMs); - } - } - } - } - - private void CheckForChanges(object state) - { - var fullPath = Path.Combine(rootPath, filter); - - Console.WriteLine($"Checking for changes in {fullPath}"); - - var newCheckSum = GetFileChecksum(fullPath); - var newHasChangesValue = false; - if (this.lastChecksum != null && this.lastChecksum != newCheckSum) - { - Console.WriteLine($"File {fullPath} was modified!"); - - // changed - NotifyChanges(); - - newHasChangesValue = true; - } - - this.hasChanged = newHasChangesValue; - - this.lastChecksum = newCheckSum; - - } - - private void NotifyChanges() - { - var localRegisteredCallbacks = registeredCallbacks; - if (localRegisteredCallbacks != null) - { - var count = localRegisteredCallbacks.Count; - for (int i = 0; i < count; i++) - { - localRegisteredCallbacks[i].Notify(); - } - } - } - - string GetFileChecksum(string filename) - { - using (var md5 = MD5.Create()) - { - using (var stream = File.OpenRead(filename)) - { - return BitConverter.ToString(md5.ComputeHash(stream)); - } - } - } - - public bool HasChanged => this.hasChanged; - - public bool ActiveChangeCallbacks => true; - - public IDisposable RegisterChangeCallback(Action callback, object state) - { - var localRegisteredCallbacks = registeredCallbacks; - if (localRegisteredCallbacks == null) - throw new ObjectDisposedException(nameof(registeredCallbacks)); - - var cbRegistration = new CallbackRegistration(callback, state, (cb) => localRegisteredCallbacks.Remove(cb)); - localRegisteredCallbacks.Add(cbRegistration); - - return cbRegistration; - } - - public void Dispose() - { - Interlocked.Exchange(ref registeredCallbacks, null); - - Timer localTimer = null; - lock (timerLock) - { - localTimer = Interlocked.Exchange(ref timer, null); - } - - if (localTimer != null) - { - localTimer.Dispose(); - } - } - } -} +using Microsoft.Extensions.Primitives; +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography; +using System.Threading; +using Timer = System.Threading.Timer; + +namespace Microsoft.Extensions.Configuration +{ + public sealed class ConfigMapFileProviderChangeToken : IChangeToken, IDisposable + { + class CallbackRegistration : IDisposable + { + Action callback; + object state; + Action unregister; + + + public CallbackRegistration(Action callback, object state, Action unregister) + { + this.callback = callback; + this.state = state; + this.unregister = unregister; + } + + public void Notify() + { + var localState = this.state; + var localCallback = this.callback; + if (localCallback != null) + { + localCallback.Invoke(localState); + } + } + + + public void Dispose() + { + var localUnregister = Interlocked.Exchange(ref unregister, null); + if (localUnregister != null) + { + localUnregister(this); + this.callback = null; + this.state = null; + } + } + } + + List registeredCallbacks; + private readonly string rootPath; + private string filter; + private readonly int detectChangeIntervalMs; + private Timer timer; + private bool hasChanged; + private string lastChecksum; + object timerLock = new object(); + + public ConfigMapFileProviderChangeToken(string rootPath, string filter, int detectChangeIntervalMs = 30_000) + { + Console.WriteLine($"new {nameof(ConfigMapFileProviderChangeToken)} for {filter}"); + registeredCallbacks = new List(); + this.rootPath = rootPath; + this.filter = filter; + this.detectChangeIntervalMs = detectChangeIntervalMs; + } + + internal void EnsureStarted() + { + lock (timerLock) + { + if (timer == null) + { + var fullPath = Path.Combine(rootPath, filter); + if (File.Exists(fullPath)) + { + this.timer = new Timer(CheckForChanges); + this.timer.Change(0, detectChangeIntervalMs); + } + } + } + } + + private void CheckForChanges(object state) + { + var fullPath = Path.Combine(rootPath, filter); + + Console.WriteLine($"Checking for changes in {fullPath}"); + + var newCheckSum = GetFileChecksum(fullPath); + var newHasChangesValue = false; + if (this.lastChecksum != null && this.lastChecksum != newCheckSum) + { + Console.WriteLine($"File {fullPath} was modified!"); + + // changed + NotifyChanges(); + + newHasChangesValue = true; + } + + this.hasChanged = newHasChangesValue; + + this.lastChecksum = newCheckSum; + + } + + private void NotifyChanges() + { + var localRegisteredCallbacks = registeredCallbacks; + if (localRegisteredCallbacks != null) + { + var count = localRegisteredCallbacks.Count; + for (int i = 0; i < count; i++) + { + localRegisteredCallbacks[i].Notify(); + } + } + } + + string GetFileChecksum(string filename) + { + using (var md5 = MD5.Create()) + { + using (var stream = File.OpenRead(filename)) + { + return BitConverter.ToString(md5.ComputeHash(stream)); + } + } + } + + public bool HasChanged => this.hasChanged; + + public bool ActiveChangeCallbacks => true; + + public IDisposable RegisterChangeCallback(Action callback, object state) + { + var localRegisteredCallbacks = registeredCallbacks; + if (localRegisteredCallbacks == null) + throw new ObjectDisposedException(nameof(registeredCallbacks)); + + var cbRegistration = new CallbackRegistration(callback, state, (cb) => localRegisteredCallbacks.Remove(cb)); + localRegisteredCallbacks.Add(cbRegistration); + + return cbRegistration; + } + + public void Dispose() + { + Interlocked.Exchange(ref registeredCallbacks, null); + + Timer localTimer = null; + lock (timerLock) + { + localTimer = Interlocked.Exchange(ref timer, null); + } + + if (localTimer != null) + { + localTimer.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/src/ConfigMapFileProviderSample/ConfigMapFileProviderSample.csproj b/src/ConfigMapFileProviderSample/ConfigMapFileProviderSample.csproj deleted file mode 100644 index 65a1622..0000000 --- a/src/ConfigMapFileProviderSample/ConfigMapFileProviderSample.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - netcoreapp2.2 - InProcess - Linux - ..\.. - - - - - - - - - diff --git a/src/ConfigMapFileProviderSample/Controllers/ValuesController.cs b/src/ConfigMapFileProviderSample/Controllers/ValuesController.cs deleted file mode 100644 index 22f344c..0000000 --- a/src/ConfigMapFileProviderSample/Controllers/ValuesController.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace ConfigMapFileProviderSample.Controllers -{ - [Route("api/[controller]")] - [ApiController] - public class ValuesController : ControllerBase - { - private readonly ILogger logger; - - public ValuesController(ILogger logger) - { - this.logger = logger; - } - - // GET api/values - [HttpGet] - public ActionResult> Get() - { - logger.LogDebug("DBG log"); - logger.LogInformation("INF log"); - logger.LogWarning("WRN log"); - logger.LogError("ERR log"); - logger.LogCritical("CRI log"); - return new string[] { "value1", "value2" }; - } - - // GET api/values/5 - [HttpGet("{id}")] - public ActionResult Get(int id) - { - return "value"; - } - - // POST api/values - [HttpPost] - public void Post([FromBody] string value) - { - } - - // PUT api/values/5 - [HttpPut("{id}")] - public void Put(int id, [FromBody] string value) - { - } - - // DELETE api/values/5 - [HttpDelete("{id}")] - public void Delete(int id) - { - } - } -} diff --git a/src/ConfigMapFileProviderSample/Dockerfile b/src/ConfigMapFileProviderSample/Dockerfile deleted file mode 100644 index b816488..0000000 --- a/src/ConfigMapFileProviderSample/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base -WORKDIR /app -EXPOSE 80 - -FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build -WORKDIR /src -COPY ["src/ConfigMapFileProviderSample/ConfigMapFileProviderSample.csproj", "src/ConfigMapFileProviderSample/"] -RUN dotnet restore "src/ConfigMapFileProviderSample/ConfigMapFileProviderSample.csproj" -COPY . . -WORKDIR "/src/src/ConfigMapFileProviderSample" -RUN dotnet build "ConfigMapFileProviderSample.csproj" -c Release -o /app - -FROM build AS publish -RUN dotnet publish "ConfigMapFileProviderSample.csproj" -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "ConfigMapFileProviderSample.dll"] \ No newline at end of file diff --git a/src/ConfigMapFileProviderSample/Program.cs b/src/ConfigMapFileProviderSample/Program.cs deleted file mode 100644 index bbe09ff..0000000 --- a/src/ConfigMapFileProviderSample/Program.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Logging; - -namespace ConfigMapFileProviderSample -{ - public class Program - { - public static void Main(string[] args) - { - CreateWebHostBuilder(args).Build().Run(); - } - - public static IWebHostBuilder CreateWebHostBuilder(string[] args) => - WebHost.CreateDefaultBuilder(args) - .ConfigureAppConfiguration(c => - { - c.AddJsonFile(ConfigMapFileProvider.FromRelativePath("config"), - "appsettings.json", - optional: true, - reloadOnChange: true); - }) - .UseStartup(); - } -} diff --git a/src/ConfigMapFileProviderSample/Startup.cs b/src/ConfigMapFileProviderSample/Startup.cs deleted file mode 100644 index a1782ae..0000000 --- a/src/ConfigMapFileProviderSample/Startup.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace ConfigMapFileProviderSample -{ - - public class Startup - { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseMvc(); - } - } -} diff --git a/src/ConfigMapFileProviderSample/appsettings.Development.json b/src/ConfigMapFileProviderSample/appsettings.Development.json deleted file mode 100644 index a2880cb..0000000 --- a/src/ConfigMapFileProviderSample/appsettings.Development.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Debug", - "System": "Information", - "Microsoft": "Information" - } - } -} diff --git a/src/ConfigMapFileProviderSample/appsettings.json b/src/ConfigMapFileProviderSample/appsettings.json deleted file mode 100644 index 7376aad..0000000 --- a/src/ConfigMapFileProviderSample/appsettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Warning" - } - }, - "AllowedHosts": "*" -} diff --git a/src/ConfigMapFileProviderSample/configmap.yaml b/src/ConfigMapFileProviderSample/configmap.yaml deleted file mode 100644 index b8a5980..0000000 --- a/src/ConfigMapFileProviderSample/configmap.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: demo-config -data: - appsettings.json: |- - { - "Logging": { - "LogLevel": { - "Default": "Error", - "System": "Error", - "Microsoft": "Error" - } - } - } \ No newline at end of file diff --git a/src/ConfigMapFileProviderSample/deployment.yaml b/src/ConfigMapFileProviderSample/deployment.yaml deleted file mode 100644 index 42a1475..0000000 --- a/src/ConfigMapFileProviderSample/deployment.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: demo-deployment - labels: - app: config-demo-app -spec: - replicas: 1 - selector: - matchLabels: - app: config-demo-app - template: - metadata: - labels: - app: config-demo-app - spec: - containers: - - name: configmapfileprovidersample - imagePullPolicy: Always - image: fbeltrao/configmapfileprovidersample:1.0 - ports: - - containerPort: 80 - volumeMounts: - - name: config-volume - mountPath: /app/config - volumes: - - name: config-volume - configMap: - name: demo-config \ No newline at end of file From 2d1d905767a282410610dc22febff8289ab138a8 Mon Sep 17 00:00:00 2001 From: "jaine.ch" Date: Tue, 1 Dec 2020 14:45:10 +0800 Subject: [PATCH 2/3] support .net5 --- .dockerignore | 20 ++ .gitignore | 1 - LICENSE | 21 ++ README.md | 206 ++++++++++++++++++ demo/ConfigMapFileProviderSample.sln | 25 +++ demo/media/article-preview.png | Bin 0 -> 62897 bytes .../ConfigMapFileProviderSample.csproj | 14 ++ .../Controllers/ValuesController.cs | 58 +++++ .../ConfigMapFileProviderSample/Dockerfile | 19 ++ .../ConfigMapFileProviderSample/Program.cs | 33 +++ .../ConfigMapFileProviderSample/Startup.cs | 42 ++++ .../appsettings.Development.json | 9 + .../appsettings.json | 8 + .../configmap.yaml | 15 ++ .../deployment.yaml | 29 +++ .../ConfigMapFileProvider.csproj | 26 ++- 16 files changed, 517 insertions(+), 9 deletions(-) create mode 100644 .dockerignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 demo/ConfigMapFileProviderSample.sln create mode 100644 demo/media/article-preview.png create mode 100644 demo/src/ConfigMapFileProviderSample/ConfigMapFileProviderSample.csproj create mode 100644 demo/src/ConfigMapFileProviderSample/Controllers/ValuesController.cs create mode 100644 demo/src/ConfigMapFileProviderSample/Dockerfile create mode 100644 demo/src/ConfigMapFileProviderSample/Program.cs create mode 100644 demo/src/ConfigMapFileProviderSample/Startup.cs create mode 100644 demo/src/ConfigMapFileProviderSample/appsettings.Development.json create mode 100644 demo/src/ConfigMapFileProviderSample/appsettings.json create mode 100644 demo/src/ConfigMapFileProviderSample/configmap.yaml create mode 100644 demo/src/ConfigMapFileProviderSample/deployment.yaml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2d15e48 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,20 @@ +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.vs +**/.vscode +**/*.*proj.user +**/azds.yaml +**/charts +**/bin +**/obj +**/Dockerfile +**/Dockerfile.develop +**/docker-compose.yml +**/docker-compose.*.yml +**/*.dbmdl +**/*.jfm +**/secrets.dev.yaml +**/values.dev.yaml +**/.toolstarget \ No newline at end of file diff --git a/.gitignore b/.gitignore index d7bec8d..3e759b7 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,6 @@ *.user *.userosscache *.sln.docstates -*.bat # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ef66d83 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Francisco Beltrao + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..87c5b86 --- /dev/null +++ b/README.md @@ -0,0 +1,206 @@ +# .NET Configuration in Kubernetes config maps with auto reload + +![Log level configuration in config map](media/article-preview.png) + +Kubernetes config maps allows the injection of configuration into an application. The contents of a config map can be injected as environment variables or mounted files. + +For instance, imagine you want to configure the log level in a separated file that will be mounted into your application. + +The following config map limits the verbosity to errors: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: demo-config +data: + appsettings.json: |- + { + "Logging": { + "LogLevel": { + "Default": "Error", + "System": "Error", + "Microsoft": "Error" + } + } + } +``` + +The file below deploys an application, mounting the contents of the config map into the /app/config folder. + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: demo-deployment + labels: + app: config-demo-app +spec: + replicas: 1 + selector: + matchLabels: + app: config-demo-app + template: + metadata: + labels: + app: config-demo-app + spec: + containers: + - name: configmapfileprovidersample + image: fbeltrao/configmapfileprovidersample:1.0 + ports: + - containerPort: 80 + volumeMounts: + - name: config-volume + mountPath: /app/config + volumes: + - name: config-volume + configMap: + name: demo-config +``` + +In order to read configurations from the provided path (`config/appsettings.json`) the following code changes are required: + +```c# +public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .ConfigureAppConfiguration(c => + { + c.AddJsonFile("config/appsettings.json", optional: true, reloadOnChange: true); + }) + .UseStartup(); +``` + + +Deploy the application: +```bash +kubectl apply -f configmap.yaml +kubectl apply -f deployment.yaml +``` + +We can peek into the running pod in Kubernetes, looking at the files stored in the container: + +```bash +kubectl exec -it -- bash +root@demo-deployment-844f6c6546-x786b:/app# cd config/ +root@demo-deployment-844f6c6546-x786b:/app/config# ls -la + +rwxrwxrwx 3 root root 4096 Sep 14 09:01 . +drwxr-xr-x 1 root root 4096 Sep 14 08:47 .. +drwxr-xr-x 2 root root 4096 Sep 14 09:01 ..2019_09_14_09_01_16.386067924 +lrwxrwxrwx 1 root root 31 Sep 14 09:01 ..data -> ..2019_09_14_09_01_16.386067924 +lrwxrwxrwx 1 root root 53 Sep 14 08:47 appsettings.json -> ..data/appsettings.json +``` + +As you can see, the config map content is mounted using a [symlink](https://en.wikipedia.org/wiki/Symbolic_link). + +Let's change the log verbosity to `debug`, making the following changes to the config map: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: demo-config +data: + appsettings.json: |- + { + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Error", + "Microsoft": "Error" + } + } + } +``` +and redeploying it + +```bash +kubectl apply -f configmap.yaml +``` + +Eventually the changes will be applied to the mounted file inside the container, as you can see below: + +```bash +root@demo-deployment-844f6c6546-gzc6j:/app/config# ls -la +total 12 +drwxrwxrwx 3 root root 4096 Sep 14 09:05 . +drwxr-xr-x 1 root root 4096 Sep 14 08:47 .. +drwxr-xr-x 2 root root 4096 Sep 14 09:05 ..2019_09_14_09_05_02.797339427 +lrwxrwxrwx 1 root root 31 Sep 14 09:05 ..data -> ..2019_09_14_09_05_02.797339427 +lrwxrwxrwx 1 root root 53 Sep 14 08:47 appsettings.json -> ..data/appsettings.json +``` + +Notice that the appsettings.json last modified date does not change, only the referenced file actually gets updated. + +Unfortunately, the build-in reload on changes in .NET core file provider does not work. The config map does not trigger the configuration reload as one would expect. + +Based on my investigation, it seems that the .NET core change discovery relies on the file last modified date. Since the file we are monitoring did not change (the symlink reference did), no changes are detected. + +## Working on a solution + +This problem is tracked [here](https://github.com/aspnet/Extensions/issues/1175). Until a fix is available we can take advantage of the extensible configuration system in .NET Core and implement a file based configuration provider that detect changes based on file contents. + +The setup looks like this: +```c# +public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .ConfigureAppConfiguration(c => + { + c.AddJsonFile(ConfigMapFileProvider.FromRelativePath("config"), + "appsettings.json", + optional: true, + reloadOnChange: true); + }) + .UseStartup(); +``` + +The provided implementation detect changes based on the hash of the content. Check the sample project files for more details. + +Disclaimer: this is a quick implementation, not tested in different environments/configurations. Use at your own risk. + +### Testing the sample application + +Clone this repository then deploy the application: +```bash +kubectl apply -f configmap.yaml +kubectl apply -f deployment.yaml +``` + +In a separated console window stream the container log: +```bash +kubectl logs -l app=config-demo-app -f +``` + +Open a tunnel to the application with kubectl port-forward +```bash + kubectl port-forward 60000:80 +``` + +Verify that the log is in error level, by opening a browser and navigating to `http://localhost:60000/api/values`. Look at the pod logs. You should see the following lines: +```log +fail: ConfigMapFileProviderSample.Controllers.ValuesController[0] + ERR log +crit: ConfigMapFileProviderSample.Controllers.ValuesController[0] + CRI log +``` + +Change the config map: +Replace `"Default": "Error"` to `"Default": "Debug"` in the configmap.yaml file, then redeploy the config map. +```bash +kubectl apply -f configmap.yaml +``` + +Verify that the log level changes to Debug (it can take a couple of minutes until the file change is detected) by issuing new requests to `http://localhost:60000/api/values`. The logs will change to this: +```log +dbug: ConfigMapFileProviderSample.Controllers.ValuesController[0] + DBG log +info: ConfigMapFileProviderSample.Controllers.ValuesController[0] + INF log +warn: ConfigMapFileProviderSample.Controllers.ValuesController[0] + WRN log +fail: ConfigMapFileProviderSample.Controllers.ValuesController[0] + ERR log +crit: ConfigMapFileProviderSample.Controllers.ValuesController[0] + CRI log +``` \ No newline at end of file diff --git a/demo/ConfigMapFileProviderSample.sln b/demo/ConfigMapFileProviderSample.sln new file mode 100644 index 0000000..ad5867e --- /dev/null +++ b/demo/ConfigMapFileProviderSample.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29215.179 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConfigMapFileProviderSample", "src\ConfigMapFileProviderSample\ConfigMapFileProviderSample.csproj", "{1343A1BF-33E0-49D5-AADB-A323867697AE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1343A1BF-33E0-49D5-AADB-A323867697AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1343A1BF-33E0-49D5-AADB-A323867697AE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1343A1BF-33E0-49D5-AADB-A323867697AE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1343A1BF-33E0-49D5-AADB-A323867697AE}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {AB82D515-15E8-4AAE-A7B7-0CDC78DB7234} + EndGlobalSection +EndGlobal diff --git a/demo/media/article-preview.png b/demo/media/article-preview.png new file mode 100644 index 0000000000000000000000000000000000000000..1e3a0cd41da5960bc4f09a3dd452f49f79f7727d GIT binary patch literal 62897 zcmb?@1ymf*w$lS>-)ajA#yUJ$cT7|FfcI4;$lJyFfgxkVPIZKyn6*bqVg54 z4*CP@s30l;Q$9)nhVH`f zyOOKU;iGjlxw^(`h)BzY`S5il`LEnx66^-TJz_vKOF}j)ZS}gmqGJ+~S6qP#N%Z9B zD@;;wEYXDT(EQWcQnV?@6~bdO%tRVBj>I_8Xv!SqufyS(q_BS$HGO8nMNYoPBjjxw zmrPQUryI@~5)E;08RHytoX9MS3&hJc6hr%rnd3-Zwo_VelkUHePq01N4x=T8lZ z%!7POvr_B7Sx#2*$Rn30S)NF6MH(?DB6}UO0U5p+(>ZMuAJPZ@lQN`bqE z7!2x!(C#%%(_%J3i;yax7)(lWt}v-@`zgi}0hbjNF#CSH05<=l2>di|(XOgiuyOA+ zYE-E(vj563?m(0oDARP@F9zZTP#FY1#G&UWye5ZCh`eZ-LjIkP+e_JV+vXY zO0|IduHw3Rmg9-2PJr5ZY^{kH|M^iGJYp77rms&wB7)h->4Zt8^lT}gT&1SsqTRRs zp@}FMIWKx!Yh%w5IQ=_5q+^Pv^@3l=Xk3{_o}FDc-Iq)y^2 zPh&vsj#YcW=dbcFHBnR8?KwY1z`I12Y3hAp92d#FZEPLQG9ED9hEqQeDa$w3hwaOs zy(-br@!+L})dTxZBImUbX;z6Eij+*PPP%O4aA3jJ%JXGd?sqo93Al)#s}u7jhHGeG z?XWR`l}%Iz<%@BUI>cdD-mmmuWYWoOkj)0Zwr1rxZEjN7u^KQNkw~$%lt8BtqsM{M zbLvPh=dhp<9MsH$NBXnQh+;-oiPs*h8Um@`S!=JeLt@5G|MaEsJ=EPnono9Ke=E7f zjo~@}-+dUJb`RLUTcwn8z`q-MY`&8(z7Ynd1Rnc*$i8fBv+WmnEqi$=>;6dol@s~VOZ zvRh#v5Qj1sDGBSyslwKQ+##4d@b#>W50*sMTdyt5&(uGsxir5w9!%=XLsULGFe7jT zc8P4p*{5(**jlDY%B5er>6<$NA0Ho=KHlFEhMsRD$@SJOg$-y?Wu!@Te;BDtn_Bjk zbJrU8X2xO$hT9fXNc8z~gNp}={AiQzYuF<{?Z``Z3^z5=BRiH-%ec3NnWVg79^X=O zvHyt)%ihM^ofeDdz~|pA2yQKZ_EyY2lRsExog2E@oBtVhR})8_eG-q(QZTjJ*0MzA zr8h8aQqwZKRHl9?{mCr{o%q;lrBZBl8tQj~?gBYBbNwZbT^HvzjJG(zjO0DW#8#3$ zpg&cD9M)_TMw@EmmNMH|3T*GkL7vGG^xI6kp+Hai%*)}^3J9b@-niLO*JW{M<^b0} z6loahhO#eQhLj{LA^BC1vzUBkt*MIX#Iu6DpuC|1$s>(GV3ixITduA!c>=>&V=Q^u zYyma9LX~>?`-`CCcn^1!@YNBswHpqH=(~0Ka4Ay{Io9xq>v|3)_(D%TdkATQdRrDV zgOe#ezz{@1x>+SGY>Y~kD9MamAHK6_V9ug%Q@15p(!>aga9?T$E-_br*hEXl&E@Xn z`E4WyIu~$p`!Q01QstYOW*r+E>JjjOfooU>@gObk~{GO zPS7}#6jIAT6K58FGUEP$o3EfrKytqxcrd7Z@%yf+7}HP6&_mFsaOuEx98=)nPMPSD zo#S`INKq3#Y#0-RFD{xmC@ks9p1?6gO+Wj6nl8x}NgdMV9Y);-{Mg*+a>a4!192xY zD_)y$O1-BWBRihY%65v}pV3mD>KzlF=L`?^UH z?XEZeu}CxHc*JS&H=pcHQA53?Y>j;>)?!e#!4!h76Mt}soBL`bx0WSs5w~1nEeb9% zsB!8KP~_S*L?~T6duCs&kU9-63kN-TU|%<&`t64bN>fV{rmtT$Y!yvs=O~`u+=C@o ztON(By<+Iat3ZL<-G_{?EorKr?0IbZ3o-cWwltj|sXK^KOE%*Z2H5U+!oGF+0qG== zQv{`XuZvPT6BUN-wV3O$CbX7Xr2TyZd4Js2H+7I@u_@$J;BVN=D3Vh+&HX;kU7zn< zCg}|*Urj!Y3jgGMZ_!Puu-md9M^jEQ@5wu@f6>#A{&QV_qc69mYaNd*WT@9NZBEi)X)Bl-xr;qhh%p>IT0EBG z;Z6Wi@?Y7q!>WHcl%8!ZIDOIC88)efna}OXWJUYyw ziK@@f+Hl2tuk09WmqlaQq`HU_vNsN7R6RyRFDiMPRvHsz1HT!W5HlqWoMWAHkAHIq z8yw}A=rySujN1AxSIyGGw6gZgAcZv#plp58aaiGQ{x!a=W-M)%AQrIP6BY|?#7hPb zFx80isWuUDh+$uQBz4?w>2vj4+Y-wg*W;it8F({d%HWyeqngi}%)^dact_^Mwr_lT z{DY(o3}odqM&^XK6Qz>B9vOElLgG!twN+iH1bVlM&njfwH2g+XWeM3&0`2QU*eHv% zVnLq;5o2c_^eN+0ra-?C%~_w`b1ICJoRqiQv|h0+HuZo4tXxuOwndok|MU&kVDfel zOFz>3j1cspW6tx4=log@nY=leF@W#M9T23GxX0R^dGgGb@7pfq` z!EEs7coeX3^Ofh#h>R{TwS@frx3bUPx`snV^GAu>+XXB=q{r%K1}$JcUz! z4DT*Kn%L+S%cHn|q{xku0+Ymyg7?CVvT(0Eo{fu(+h$5Q3IFhZ5cj?Vx#aw2Fjzmq z5v-rpBV{Xzkm#s2XnVs9DUIf$Td18n9e2h~9xa@vcLE7;xJs2r|G2v>fzFWaX@iUJ zQg}5pmbbLrh1D&#FdGKAg|1npUWa43;LTu zM%{#K&IEdCN#soh&d;?Fyx)DX#&XK7%n94t%osy4N||K$7o>T7v=tQd&Dc&6D;;K0 z@3uK}C}$GFWn_z{Wl}h`{WQ$^k5}4Hg4DE!)iynAJ7>QvE!7PWs3!Q0BcaPkM zTux4vMt1`R2X@6lYVFBCA(-ZpVU0kyRgFL$zFpr&!-FHyn{f-KeD>pEby#|*i(194 z1wIclZJusk8z2?pN`1Grp}01Yz)&lez|KR)=E?Acp9|T!wj?krL#{+~z+SwH4ssb3 z@a;iI+MIJcGPjLBI0jg7#S(Ztd5Mw*oiY`l4vD3cip91yhHDM{5*V~K)i=FOms@-v z0ZAyWHRo-0#aHHn%0guMt--T5c8Bnp4PNh?6v%BsQf>)-QIQ=wO5Z9QrY2(nSXE-P zLRLGRU?NpSA594{?T-mXRye8ttIf2Z@o#sZLPxTMqxwr&upo6eD89l3heVx&rLA(~G%C4;WeQ9o@ly>RxAJ$#Z*$EK@ z*N_Bv9+Ig2LN#lQIG|Osc{caZiaz7MTzvUh`isXILbBp}>n3*SpuoU8DI+M(4}$ag z;=!+77k^UkaF;SU06KKb%S_$rC8yt=5AMe~0ytVFbS&T=TUi~XnVAUka{uP>ps!LV zCO6}po7|5vpG?JWGx=a6xicx6i=~=p2y-SZ(%$_>*p`Pfi_Anrve%=;3=XM^%cr1n z%%LuiVE*#mPWpppxV?Bff;IM8^>pf(fr;rrt}le*>Y!N|1P|7tJc zA|fnnzu(8G6&;0FjH^(!Dl|(&728~<8=M&AeU86aLb7j4pp12$=IbTkEft9a90~`n zez^+jwCle%f|1Y{wWY!rv{mOYZ(MWvyyM+tR4}5(*^k0muLIZbCS|lt8bd;(;CE&> zZ`Lp(3CDK)BeW{jG!nC%8a3$iV0j2t0{Xno+f^+zv|QQw;cgZmtC`X%DS^RbGm5(> z6~izCF{AdHM9-njG)d-|`5LtbCW9^*rKV84Ej|H_WXjHGkbN3m40tN zTx56HqV{^)*sxtV<|03N4Ek3Uz%$x5j$&`r(ftyk)Yxg0)07rBH{A$&SdQQCFL^b<)5?B7GI@`tZgj~0lUd3;mRKkrU2)p?de4U zQt(Sa?=3OqW`-NzIV^$=3aykG6*vx4{gOGsKlB5HTyN67O?Xz)xzn_KJOUqNpla|vbJ_=@_JLA-j zj#%z4I7!pUI8JnbTf7P1AW(gr4XXyHmjwg!1=HIRXsjNY?$zW}Nz*RNztVSX-;n{K^BeJS*cux+6sR+eRe-YESaATr{}mF1_DDSD3<0pKgQGV`Z{jym5$+a8@@* z2%XVwZVYTXq2X*WL25MRuUmTVhxottNQ9%-7KCDJne&<$Jk6>oubR*1PQUmxF+N8t zPVJYM1YBYnO=Oznc3k%sT%+5RgTfJKfZhq1XJyvtv9GH6nU+Id+NnJJ28N$H4nb-y zBZQj{*qbPDJf7k=3jFEr^lpD*C3_{k)NMd)eh9m6Tw01bjNkrZ!`SQHOW!N=zJc-5 zo5C@N6xf`AM{JS@#144FtVfS7w_kg?Z?3GY$@SyVwfNKuo4_NHIl~# z-d_F=?)~TXr>N?jn;v|z?too2agehu|6)@13^?q-=IFTlr3TE^t9;GW3B5F4U2&Qn zTIdZ(U^kHiwAEQ`7Tg7Dcof0X~y6e z-1T_~BCjei4C_$4cHjYjF_lCjY7iKQ;I{R4n12g^bZjAhBsVRxCt`!(Y4E3}s->l> zBNB_l#>&bHRV2FcrFk`2X!GQYB8H;oE^LDXLa^mdG*o5yxpuj3(yzr9gDJ6xzKl+EIs7&ql| z+@Gd-D(1@(0(XI@Brn9W93<_uC-g(o4Q2jb@Gm^EL|*TQ{yh_f_~k;ru~IxH7$Vb%#oC*P zh;v1~8uBfm?7%jcD5TruQJj3hB&^<-4laGo%XDWH5^n=HS6n`^7TOug`cj#WnEb<- zxfpOE*c^&*C1320)x0Yh!?0qohzg*5+(tr=#~tjoGCB@<^N-&eLJSf1QpAk6y*!D~lDM-ZKic_0ac+F_QSmBu0Si%g+F5%!Lq zL?(}0tleZXuTrmQ6j5Lz>-pVp0&4BQ-C>%5vuKUat9XZ~UNV_MruB_8nV6#bJF;rr zFvUMOVC8b9>&Tqi!x4e8jm*kt-;D}cMt21U)H)LnN)N{7lg-t$qA1Tz4?JB+_j?ci zOJPpj{(*T9C@!+RtYOPhHphwNVq-<6qcppJ-7vd2f)23=?yPpXYw)`7 z;jooU8F-7pg@qbZPEU-WS9z+6y49!R*A1Wd5+7$oKn|zPxXXoxG@3y5X-8fB_{RHEtmgHfl$;KifqzSF2g$dKa z?_m4y_XBEXTEn*m8aV)hKXK*7ha`U-8u=|Z-x(0qVGK7nElqyu3*E@OX);Sx|MXf~ z#tA!E?c?AA4+7yVS84BU9&!eGj^0O%%zSe29RK}M8voVU(*i%F^=1;6G&Hy~Iw+Ur z(s%4bDc<%sL0D?FymqU-l3-Ng^D!`^m&qQFc)J)^+<4v?-3|_yNc1~aU% zK!;l4xh(17JSlZ>ysQN8ApM+Rju4#JP~ZmA9y8M3pLSLsyOUN_h+EG? z+V0ct!sGbw%E*7#k!HTmc!EC1h`_HUP1w_p>Q-z<3~XNwpl96LnO+15I|wvP;1CcX z_w9Gx?tQv4#(lwE549whHR)0Q4S8jWcbu8}e?VSqs-VcL;sVbB554z!d^v%o+R+56 zI!g7(6l?~`qmbQ~*+}yK+vpAb?LNQoDGIgUoC*15LNq^~GW>*BipIPy^OJNo*xj#? zpM=pc!E=1V?J`M8Wl}=8 z!MK^Ghy+}nqR-SI$HiZf3 zIP5PGG)>P4ND4N$)}i@MHg_8i9dlm28gcq=@m~i{aqkj^b^N+3#H@s~B+<8dG7REn zIWzGjXk$#dx?W{WN|?3}6e;fSs;@)mS=;%8nz&b%C`Rq z;6aG$B50=FHpuMa=TnOX%3 zRicG@8+TDbR2>HWx)txJ6a_w3U*<`jrMw%YdMsn_0ayWgQ{cu#MB`e$0XF4T!!xg2 z_=L;;bX?Ra-_O|hqR|TnE7=023S*Babsw{%o*~2!iTz<~w6;D^K9KR%#}P)E9fP*J zOp%mt^NDkjdb-{arqaEUNz0v@^S1l{0cP6pi_ho4J~q7rJFKs~W8AM5s}ysE0D* zd-#gi<2Ft}jyam#J04Ks$|3{rNN<&f8UA3XG?nMf;~`W1;L#{(T|-kGPg{3$&&Q_j+3d1a(5Qg8x3BHa@pqKMg{}S6 z@=WJ&8xdXIQBZoji_HQpDMA$~4R$U~JMHBDz3tQP3B+hv{Tg)Q23~4gQxmy9wsmC` zUy1TwkBe)E9$ds;d3ax=A!9PT%(~P9Rxv(1oxq`7>Ox-VI=anaI-3J<#)iY!ZZs?mKR(NsTavsMCC|658Caaz2JFC#s4eOu*7CidOSjb>`+^6W^-$Cn)h{dC2Te znP1Qisr??Jk*pf9e+OotEb>`2CmAb3^8$|V(C|kQ^UJ^QRIr>QeL?t}^$#xpBK#JC z2hk3aWwQPUZ=QcW*z(JOwfJ@F~fiPuq@6GB&FeF*h zbjvx1Kz81vf7?|6UKl&BN?l*%`;5BfVy4Y6vaOuuhmQ&w2-bT0K|?ao82hT+M0ziF{{NK{ zkT?DJltA(Dj?bwm-$P-1rM!jwrNRBSYUXol;f;^=O#-&Ar~F%G(<~45#q(v`$Eekd z@9S~TI`j^A=VQ;61U{$!#JBBlsqY-vpXtv$74{ih&-YJ#?*`2;$Q{+gmY6dnK4zeH zfkfgT9LL%wpRo{(vL!CHHhr6fG}Z>MKaw{kq*Yk1XY35-hwZn$z3~wxX3ldEcP>Gq z*%R^QpGGoHz?-H9p*~U%EjKYX-|X^T^{j5jMf9F{ohkC)tk^o*CZAqYJ*|7R9jWr= zw|s29Ez*UU(<|xT&8B;G7aP{Pgf$^JWtyub;rASF648#STnd)$rG%Z&4cqg``;Nqs zBMv^&dbR1$o5=B`@^t6a+S)T@u$YVNSC31t1Trbf9dWT0QEvk_<3ENo+!F;C{#pQ+ zycMRgApSjr6KylXTSS$`9y`Lk99OL69+{HE8{W{|MsF;xZEiFMiGquDe3bH;{FOVw zo1he~P#3T3oS)}>n<|vl4brajmn&F9t^uw`0tp7tu5b0-)wZ?V=S`WWmK6N936jp2 zTBM+Y9~4RUBUYYT(3^sbTxUt`hH+$~I9l>we2*4I5^2vRKAZlR#0cGDjC@}1&vmNo ze0QAQCj8uK&j({uK1H71H#;$CO*Uz}jhbbX2~xWX@u}k@7P(o|Mf}e`j$+zqg`#Pz z8FUjhyUz#s?!jz#o-%FQYO=i8v?pgR4O@yOw4YT$ID0Co9eL$}N~>9SDo1knkgQ0~ z#ho+ASo2LV>Gx9Ci-uOxEEB&{hN#|y#eqbu!84)9EQ_xSXlLycY{~YACE1kOR%jnx zJ8pW#VJVqS9C{iaDb)ANZ7Bk6AWP*+(14z5*U!o+Mx`irY?-8TMq!D!Sial*Lm)k| zf#k6}3)7zy3B>4XkHc>&1&N`hI@#soCWs=ufN3aEqHXYBB1-ZFG`XD4&a;@^W7sy& zWS>W#pXfEydlnVm&qP{ zHbSO@EV)v$*z+s>t6d1T)49rNbHNgcRGkE0hZBuXBgMb4OediMd(lF7WjZa3Di1%B zvAq#=ag{i==o*)CM#1L)&rmOklK+u<3AF|*T^v09!7`E^nPes9r7xz)M-*8-Px%sR ztz*mg!R#VFf2z=_9I)dikaU47WP@K6&gr^FJ+XQ#=>vW|d7cCgf-7orO=W?i%|E8kq_dYIK+E24f&sd=Tbzhhq5U*p9t z0-XQpu?&#KXtoYn>Wu~ROecBK2Mj~F`&LtO-<*hS872YFxU*FnVmD(A)X%}l;%T=n zsgn+agrYv0C}d932d0Rid@vzjol@0^=zerQgO@|7Frtu;`iF~@K^pywW@VV|5Ehj| zr)=gwCcm%KF_XtG7~2dU0;aHs7T>-e{j#kKqCce>@mP7(fdCxVxM`oka3?ljWpiQ_ zRs|0{zB<1iTWSr|ra?oumH*F@Jip$&kUV_O*N$WVTastsYM-PZ#)hkTiO~9^yXZGB ztT}}`c48WqD}gd9*=bgDiQh#ZdIx+vPY2-0W2;-w^8!Y)3>eie@Yt^!S#hoNW_zuD zWIDGLK3_E)gOX(TJIx^ov?gqG0xs`7<37-UINEYJ)NXTCP3pwwJN0bHwQV$*$bZ$~ z@9;^d@K#@Sx>9pYgjV69a99_Tdi$-Vm|ow?OEf;v6@(a)m^eu}j&672+ekj#%3^&v zb+0}bP%Wo6&kLj+u1}QG4UaA9+<&04&T^WLEecD(P1;ExgQ^Z;8LJecsJ7%EF=!7B z6q1lx=mDk++=s%1p_5lBP)2Zxr-UH0cqG;mV<&v&+mG((MRv(Dy|@AG*tRG(PaGL< zzSShdi$bx3RJY|le=2$LRnI~re2TSqMeOhRy5zLC51HBjl&qLN9Hw-MbNb{-=P^@E z|B3erjiWsa%N?UBx<8KkT0feBtNTQs5o`;Itk~J~oK|RE8U&k4P`T9X#<*Lg;>Yo< zxEu-^an1cF*e6fZf6qR_ul;-WNz5`xErVZi-n3Ce0maH4&Qa!xzV{FLXh40KtGdOA zDN{|&*2FiWBID@QV?PR~g)c3Zroa{b9c9D)el}Jq&8%{b;>8{DjY^5!w!{22p%$#p zX30dr(esnfvd8V|I`LzH?Gt=U)-lv|LNt37gY7GIZyN-!ItB|NoZAfMq%=F*3iNGSzl_ z@;fGz@3G=(@kS>`QL5HfWcfx0dwOemQJ>x~c1YrYj(-D z`SQrt`XvKOaU7k(8+^|dsb-lhj zi4S=0R7aG)A4KvQ=;myuKqO1_>fOv-EKqpHQX!}R9cD&6h&jftL66W+>18ruKI|rU4 zMh1`i`!_GB^0SA%qAm_i)5HKD&SVM&1;v~=cGZ)Ezj*Pf_Trz{bs3k z2b?SFg6h5FBOS&b#3EZXrOUbf_A%i7`PM+&Q$o{zAdb&5nZM6vgHFz3e6!akhk~`Y z)w3Z7!29d#%V0i57(Q}&e9XiggGO0hVg1qcY=BG+PN86Aiq8-~r{m7W^C3%{r0LVV zF64FOT>C{3%?I_1W6Y%}pcK19{{w}P0)bKP-+2v!aLwhRa%6I50v8!7zt7PID*B*reaXkI=gRl-c<%xmhB zt;S;`6e(hd>ZMD`u_{#Zsmoi$z+5HrBNmRrkh|gchgNjl_zTMQP#MB2(GB6F^3xGh zldrvqN5rxH2n4i1pAd&|n$14%qw-3}0~D|fh>0?II+m91DtuGKR$7Ogp6c;{r4%Yq zz{9epd)gaE%)li>kK&-RE8T;on6$giS;H;DE+B1lWRp>+!~RewJ^wxn(!IN2s(Nv) z%J&>H*8HU=qO0|m_C(s~Q`rAR6<&V``Cn0m55R5(ON#%2DLmsI6K)Qmtyv6R+o7}i z%NnN$XaATQ3-3!)q)Jb)>8TUaPBN^?VL8jM@5>zAe<*6_6SNe!)~@hZh;BB{(W#pK z${;^IfD~WY>v<pnu z05BL{@8b`zS1?$qri(NxkhgFQqG`A<^kI>U40h?Ojguq56`k7W7slvEir^&sIm0%p zqU@d@f*g=m!h?5bLKf|h@d{DZm;1knJQYD^w^ynDfCq5eW8xbYv{2Bw-Yj~9g|5v# z+mEJ+B{GxsU$0*pgAv@-RH}EKll+oHP_$RSS8X^98utR4p1QR9(|1KQM z=LH-8m8@v8r21snyV!xP^hzb~FAw%(W@|c-LjE81EDRj%$v^7Z%G7V*mplZR{|n)G zt)oV?74e9^?|$%?4rrHv$>)ZWP%Bz(i#Dlqj^Y0Z!F?xU%Z|PGL4*Te!fCexpbVsf zEsg(wryQjU+k8A48rWkRg=!Xb4vu!(HY2;1S5E4;gd&jYZE>7~9R8B1Fh;(d+N7y; zgs4%a2#YEuj*mNcJx%z`uW4E8X5+0&W?CAB&y)E~Mk^e)FlF0j(P&<-q1^&nOCf^P zQJlkO$@^XIgr4nEzZTVBax%B-Zob*w3x4V!3$&-9R{%6YP7{p{gVIrv$L9G>uC-h* zirt{cNAHHs*^M%pITT#7RhJj@!|26uf+ZT99*eJs35m+%@xPUnEJs>b7}`@GQ_Yk+ z42a!Q{Vi7tPo)jkY5h&Ha{I)6R|%J4$wTGoX{w*+P&mfTA0Fk*Zj7+BGb$&`+u)&j zz0s)R5+;ZuEru?~={T-mBu)BqE*LC7^m2e^k}HFwXGOe}GRmvNt)_2|of}cvNhQKM zeg9CE)Gt&e51NsPhbO>tDIo91p&L@4;Y)QoZnYRJsU|AE(uO+!AE_UB7q!wGW#ubS zz`Tpx(`j*RjJOCxEQ=36rHW*qH4kNm;6i@ zk^xS##Q~2jjcbd~JgsZzOwYLVh|kll6f==u!n&^nj%**G9OdhkmKO%?2j*J$Js1Ur z@}ioeyBGr5I8^%br=+Jr&;vgkzmeiJWPP;v`nngs$!PgcRaF%)&i9>{;jZ_lJ+yY9 z*~-=>Yx-D-xc+je3y}al2N}6^q-iJY=gH3EareE-NpNr4p+*|>>FoQwkfLe0tiNov z8m0_1%jG*?rqN~VABpRH+~edqs^4=VF>2}QAGUZ-^h%WTI5#OVeX$|-8X@Q@`i9x( z8w63oeZgv@xH*UcD^# zXEpMe;KH;1GhIg(=EM2gqwpEuU*3=!b_XE)CF9BZDyX4bh;d>c(BSS~-LU+?@?!9I z(jS~4^sr{Oc?f%*@8RDnKl0b1K^7E-Drj>^M-KLcIzNI6200*A=C!*OA(8mn-qFX1 zb#19VsCm?6#CmzBZx1+q^2K=E&W*C;NsZIa9VN&LtJj$+1su*AC*OVkWz9O;ELjQQabL@yGuPXR4+kRvrWM8N&xk7+q$`-i{|91WgkeQ;If=$c?@a6NlZa zkhOB8YWtcz_2S~%2OR`<0JLMAU=zf~SB}7c#1v}kYGAa|V6m2}elqfapjP*X+bV+Y z?A1jESWXfI!~v$7UwQ2hj$FC1bGW})(tW8%;V{Buf7yz+*jok4XDCkFG7ga!fIA5DW>h@mwO#ftlBf*7xRL+Z z)ByFDj1ZZ{bud!KuAUGEU7K0)=bD7_xro0`yggRHVj%JRr7w#5@3R!3{-bG_z@3z- z+B}pCKond(52p6yQ^MjtC-}-aX6CrIDEU(oIx{i63RtvOsK0pqV&R#w7SwlDNEdYY z;upIKL-CJVr2TW;2cw$46j(07yaa~ti^mU;3n*8Xi|X(hKh!s&oWQh;gj1VSIW!%E z(?K)Y0XBnXQasCTuM-CHucynjSfWbqg-kv^T$l_oXTLebY{Ps_KzKU`70^rN!3LZE zH}Rt~zOo>FJAmQ4`~VgIgJNEC7IgH@yHj-Mibm~)duoTu-9xZA0I~xFwsjG@FhK@LZUs?TxYd$QzTCa>DP? zzWWXxTq>`8&IZqGV-d7{wg+858zweht`DcM>>?l555W$`JlK~AdM)AQ-Q3)RMip+( zM@Q=Fy;0R1k9G`PEUyNup*oe6$64k|ce+R~XgYxPo$dN6Ov^mv@~FqD;l8@ogyPhA zNHpp$m^^WJ@ncXmGJ~pz*9_~!t2m0QJ*qm#n z5wtnc<~R+XmE^A=qz=Bcy}9&Sfzo!!lWA$(lM6|SP!SMR_SS1~10H2#d&9!FxJ;yX z$SF?u)2mttpLkW>?m+X<6aI2TW64Wh!?8m>)>8EAYZ~kwc`YG41_jki-|1vUfPx+u zZbo7BCFE?g&%AAt#r=f-jy*JyZwhxe6Nv!T=Vxc(x-EDYjDsfkC3KuBkH6Ho=p6=U z7D;+w&KMx5l{BY4ox&UT?c0JjaO&9f(33%e5t>Q`_sd3%ir4ctx)!^LBDOe2FNdX63v8<)7PCT1 z9j@~s-&=+%VGb%8F+jI*zhdEPBs54Wbd?BB_M~+7l<4 zHhh)$x-{SVPmr@xWeuAawlQ8z%woq*A>ih3+G%}C@RScha|_Qlj2ZJ4!^h|y-@j~_ zJ;HOCrs|{EuJegrkbuRglTQw;)yO!&(2iR}H{3W?F%T*3WeezO=6H9j~siQA)BdSvHw-_{9FQ@6&a@cPhWlTS>k|D;RG9<>bR$Hq{Oz z@5T<45Hv~V1%(GtT_CN0mYDu9vwPHPIiYv4vAu=5T(%YEgAY*`gC>1UESwd+1JU-< z+LHO)O9jUNV!6<}q4f|yPv`1d{OwjmHp55-;M4y8C@4eiz;{(UR_U!|IkJnUH^3gY zoci?G-)1|Sao3kWtWo^>WI8FIxQs3j0IrB~FNh9^`lO?*G{C4k-OJJX9CgK4^QL81ppE4e&cBR=r9I)VQ(Vf05P#S8dn1Y(AYBa zqkp;Xs18Qj0QojAZ?P&!k$KIgY$xqd zH!p?>2{zjCIQs2fTQz7qzXdO`nc+V3_zq5R5NdVYHbqoaxzP zf%>;dzk_3I7Bss1O-HzI61+4|e;g#MLqlR26jcItM}V^+vnq1%^4u%$B-jLMBFSPQ z=9>(<$-PXJxn?M_AEYNunCx-59!c2G{mW zCYKZOV0I>RY=Dmks-}SIa6jCWGdX@j$K*vUU(II@wv^+oG~PbJ=F-L=sGIU{j(+oE zZ-qxQzIZ%qtY)^yJ9)KLEffI_$JX2k)qTn7sOdad4+*N$B5Cj&)O#<88>jn8_-swl6fvTm9k~D(u5sY z8-qHr7(7_6%U1UhL`qG}3X;FfdGCZd@50cm#(t7Pbi26Ae7LRJ8;jk_ zrdm&o`Efe+qAdT%bdrgzRBUn{C8L&E^x&4VdYBvhbWpKz>)VoTN;(M67%68JPVB?u zYiIi0r6+ZUmH2guW0xxe|LYao@F<%RBNMk#4l>NNpQTD4ZgMR zq8pVHXPt??qR{0YR?Oy%dayLlwL`oZfOe(%J_dU|cSNDKz=a=iFHQ2Bp5>@Z?U}R} z&D}?s00p3lLW9H&RmfBcElxh+v^D%v@0FK}3P)45r!!AHzD|#tHtYV)uP-xTVC*iR zcyXwLb$k?$T|KtBRFo}#;Jhi4PI^^#qF)J4qWh6um7*!8wb2H3=JNm|c-?dVZ_4r$ zTon7zWU7=5|7Q>fc5>`oBqWl3p@>nQ|JAo_)6pKb!fbZCA`1ZS^0v5YR5?vUQ#MGA z*LVU_urzWB&pFe@>k3thmf5*LBDuZ_ z&1(l6u||uhZU@rRt`<#6i{XpQp`xCYzT~jiz2valKG3VHCC*B%?TfB^DQXFVEO8BQ zgG*!AW~Jm<7Xf#RC+=;!LwOZsSNUL37_-_*ii!RMiUczyB2M%JYw*>3*z{ zFw`+tCBAy>%h#38tl;_>|EkoXYP2)dqSN6*(i(-$wo#-+GwA_Su@P)`36sU}1pmY6 z@`>J>#ARjvlG$Mdyb3qc!kqMcK=7D(E}V+kGkff-e1#aC<_)Pj6ES+GYsTtMa&glb z1To$ksgUR==(`iNp>Gy2sVw7lpc5sXLB7IwlF-;N8{-!dRU50#4FLh?3L}mQ4gdg> zcTc5jNuOqU%Z8QX%d-`hoD#D=@f&fi4(7Lhl5S2CEGKQE!PUi7cuDYWc9>{fBy6^v z?7!sQ=ka{x9VVkS8h@(Hyym>|-V**v!6ujM+95yRK-djCj@q1gxjER+A&dGxRTS(m}6 zp#P(~h566Ih5we4FOXnU=%YDt(tj4dzV(u5aGgDKbQ1h*!3Pa7pZ+T=w)TNfMF360 z{iWGYEp4kO&WXSz7n}0Bw0*(9tFi+X7ax~~Ih*}uKlILxdDD`nm4fFWm$#nHzv-O$ zygckdN3C>a>Ij|-=M%Y4Bq7_4&H>q0|Bbx24vK4iw{;T;5IhhhcnI#n-JQnWA-KD1 zLXhC@E{(gpy9N*L?rs6jBx|kTKBxBHci&UDZq})!MtV3u=N7xAETyjCX6SxvSSajGmEvVMY=E5G_28=!z#x| z%DI)Sn<(M2JZHMnh9%(hP_%Ylb8AS2^BmDVp~(q>b%ZC;;2+>lt!bKqW4|kDkCL43euzPQ8 zOd;&QlFYBmoB_`EczKxhXkE1ZX(T-I413_vz=W+N1XSbL85Q1 zo{#b_3zg*jgJ9pMbjU~+@(^G$Y+bUzyqPZ(1{ySf{#X?YL2j1)8Q!#pW0sn@&X~fkq->uhDN6{yg?LFakly!fSCr&@FOqk~$ru$J z7@0VzS7+4tt@2)u%YZTijS;y3;yEG&xRu3m%Uyn`K=7T4f|=Wr$uWCtn#}>+BGI$A z9p=En#mnw+x*l!gOKmOYl5r-rvrdy%PsIEiVQq)k2byQ%E%bXNl9}#J!l2i?&Zdbt zqa@KP>`w7wh*+>P+gP1SG5LgrV+)?MSsmLwLjwf$8UqB+Eaz1b(%$jeRT~N@jVd83 z%fM+m5srf2c)R0RY-A5)^T73OnQFJB9nMdGN`@vm zuy_9+P#l6H7%$$butktNxG7_UgRcHV8#5Dlfw>F~g!-!_qJGN1fTIWnw}H!t9`WJD z?=9trh6B#~)UzV)xM@Dym&nf%NxwbZ=8Y4*6g5V6?N~pRa-;aE8nK z-Q2>{R{}1w82W&I(v&oqH)X7jdlxW;wmCV;1G>!4Np` z?fceb1USg070mt&f0Z8@B~kaTf!U(;Xb`d)_T1Onr$G!VWOQ~Ko9VvoQ{n;4&G4qM zhy?cPgB%1S$*N>`DxHdnYmkzjS%whK-S~RyVh;;bqakR0L4@#HTyL9?l+q1Nvkhd3 zG@Ie3J2bdL=g`O!Wu6D)N(g_xXPOSaANL?ZV%&+rRuJO$Wq2p(XJ=jXcHnlLgoaNO0Q`=5`cQei4i-rJc1Cl~OHoMHe6%hO zLcT98L03)IzR?3&c}Pn2oIXw1PLH1W?g8ddU8L@@O0$`wD|Yc@pB?(BW#t3GQqsWi zd5X0NB@2q$!#kbdeC5x|;+^y4?L4u_^ZsfnnNcQ%BnO~Yr-VEKd;GiE36V9l?dY>V zhgZ5z(PZ%70H*k_?SJ%G^1VE8(t8Vb7|F4UJ(fYuQ&IUW!I{5+P#b#ia9nKVWO#Q7 z_*j~QDjH3uPWZnRPCAl|c}6LsgXhT!TvsjVCKB*hSl}wgJ~XrG3(`QT&{JQ{uj%ED zQqK2x%W*wNqfd0*O-=IbEQkMw#kY(DGbg5aDK=WuekLiyyTG%oRM5%tdh=D_VK2yB zTF`P;etMUoeki&WUrWKtS+J?XZ7}OXsxEmC(XKGS{nW--Ut{Hz0mqEqbgoF`m1gRI zmOGk}E`>`uCke?y{u`PGfw;~#KxI!3n0$<88iH3*ZdJ%jj$nz(z{g)!BlJAqGf%%b z7G1v8s4S;0No=1E?|jIu_ivnK=mipPBpoG*879?xmo+O_UTtyWI--BRLqAW0n5wkp z14XtX87r3KwA*Aw#FAQnHkD{bXMNBJ`yh3Gly?1fb_WfOiPsXfsZvS2><`t)x>(oixD2D)+gMCkIk)iYI1MInkiVL@zrSI9S!YUbIB4a(g^oLR6-|T?K}{ zNlEkMoHkq(&OGSgA~rHpVTuQY?;A9~T}^PRrOGEgx;9SrLR&ct~5 zPkDUF{yV|Ehi%DaKA?3~Wj2zMah618M>(h_;e4CIBBASdX-z~SONk!XK{cvq?mva9 z|NMzc5(xb#ew|_*ig~-I19~+DHTJT=nkVYBBbHy1ZIjE(Aql(wt?%G_CCQoetHj-N z{$CU=I4@YerO81};XQ218ZBe2|AL{l{7@=AEush-d_UgBRVe^{gnaN{u(L#3eE9=% zbth>lXpA%>=MarAKlAqfOn^f zDnRY3rhc)BI+0ZM`h6jM&m<5+<8rmTKAcm?lOCXug;K0|*_j2^0J;F5!E)V>PyJhu zc+udp7@~mcUtVL@=G8rI-wn2@++G@+tBG%Uwy^L zjL`En!!yuFhj`-jt2O#zNUkN3%E-rTqb`^pn79=%k)@pBKmqww^7X$` z3s#>ab1pDdi{8L4_JtED%#EdSyFdNDv)>twTqGVB-y!Li%plF+DxZi|!2erTd_zmrVd3>B9Ja#{L9Kra-iVd<=~twM&*9HHebN9?RHk7ubH4?W)AQ{&Z%qRWOMB z%?rnG3{LWOWr{63G$&WjdnM{jN{qjPh#z61)YyH(*FICvvZ*?dW`~2m9 zP{k9YOXGBYS)MVb{UoMI2AAw{jr9*!_zRFiGV6e$83l z=nFr(!R8vL>e@8_jGQamSYqY<%u=yZqw}CQzE@O6!yq_{G^)@>6aYWuv!l?mXslD` zp z8q3M@U$NgU$Uj7CJFz$qjsaVIfJatCwUG^3H}%0{U@=al^ zj`qSxFjUQ}v@2io0jcYZ1a0Ri7e`uB{oUr4NgBVY9x4AEMl?I^n^(4APM#n~Pr{9o zZ`mzJh*wt=%?VL3^=D_PP-7>0J5}~lQaMpz3*?}4l_EdtKWc0?*b_^v;dFHJy-ytp z-Qe5pkz4%U`mL|bDJ5_BAPd^}YK&lGeof8w%jfgUj*q6WUl2bX>jcG(zN_^bffd)0 z6JuxA*pA-UTzzeImgj=OnO*{N<1je>eo|RY3fN9mV^E3{#<{sO5w;NunBWNB8-B!#YpnFlq#@WiVB z;)&Iv-u%{li}!uB=+=fA@HS{HRM_1GNJb{hkZQ{n<|0|+A&3l^Xrw;PG~!WerN1b= zw$Je5F{C0!l)+0$d(eXnTQ2>Id=y6b@=ySt6FB71xM%Is>tVeuAVIDiN*9}cHzDG9 znavgS@PW*vCNX^ketMc~N71l)eC4D-K0JWa411|M0C(PHDv!^v5et6)C&5PUzWD%3 zPYn*XaWhXI?f9DkQElbdukfh{Q>^mZ+7`0meGy(GV!xgT#Q&hO^bWhD(|uElQw$q; z+2Y12)%+|eo`XrT@YF7gpWPAq3Yk5#|1QRVZ{aLRo_TsfiaKFcH&9kFJ*HmYb(BQm z!LIm%JsPTQzwkX>C?>Ehq5#De%807fji~%c2lffM-ZazSocAyGL>b~|n`5qqHmePO zjLgbkq_)O2_^QADmHE5h62`DIj4gvli6@$Wt4koVP=+=#8_@BAuT;4^W3BKGh7B@s z34}p!Se!4_zjpJfB|+p#HzMZckns%0G`7yv%UhRTk<7 zcXO+RTc{gKQK$>Ynzf5z$x&Wqn5|3HHPli)E`OpyrW6UqFf0GHn80<8g?L?Sz4s-< z)AIiEB9#MX0S9Lx=5iAx;Yq|UsgYl7bz5W-m8LxKxfnsFGWeD9^TLkRKT9a0ExQCCD(IPiY8U(c zxVLQ8gQFqNU~Vxl!0=E=*XLaqUjilr9{C1Q`XAF=lH3+NESY&1<=Vp08a+*dP-N2? zCQQd*+%q(o+LWHPo2f}4KK2kiec4#+f~KQ$zb-zzJ!42ydvyYZK^unE_#zR%$^`}8 zXo1HmODl-eO>H*`)TT@iWG68X>KcwIpukDSrK^=R zFrpkk-5$4eNR%lDo_C^(jExozu3XcIie#kUyVoUq8lbDymqk!N!T67;uLdLxa5F{M zF{O0O(;l7S{d>x2vgs3?3|jS_1;%eKPI^c{-A1yxnXmH^T6#;*b|d8@E^h{+4Y}T{ zG&(p_J}zf%n_?sy*+`KM+2>NM5wdv4n4VcZM3ssxmAHSJMMgdsz}TF8y9D7!hKY8w zOOE-I8iiedu>r-sAA1?InHdI#fq$CFe)%Li!-gVg-qH)nw>npcX4+3>KX@wqmQ*h* zWi$6vt?Spkky(X{Ku-kC1?)Fdc@6P5-l(>*NoL=^1F~w-!C(tWWHGlr;VUNq}Y%J0mS`8iI%Ka~E8ukD*!UI{U1KZ6s zNFMEB6iTv4_-|>LuE4wQP9EvCkMHc^z*J2JOJO`C_D=W0zE&pvUecYAjQvKUvy33~3>Q#*+qbYy@do<;)eT_2huA)B0TJn}HRGi)@R*VBYaCk6Z}uy2<+Y@yh2$H(a9NzX{{ zn*kE>Z5JQ~bs}G+U`&~(-<3h(By-_54}S`GrK&eq3O302{Ca@sAp}> zTA}7_F<4~1S9~E*36s-AF5Hkb$eD(5EY%PqO0fGCB9v4)GCYQZ#pP-B9MMfW6vI&9 zc78o&8z+BGoCUX_z_}kKQejnqEYxL+7?2LONsbrixl!-^@I+WdT+jTz-fSRua+W9m z;ZZ#oAFp7nfdI=khsvng8$Nd zNhE_uY1-mhV-)#GdNpV?9CN8IlKTQ@41oeLfodJ2=5AQ^J~G?o2qgvTS>`wuR`{uB z^(?Tpu8iNRYYppK;GZLu0BNyYgZ2ggC)Mo4rAY0U+gzR%ukMmxnkw(N?C5%K8jr3V zQzv)%1%8YK%z8N9&Kgg#hlu*YGN*CTs=jS@w{1YJAxHIeR(V2w`%2cg6*1vh&{fw? zUwHM!=*>m_NGU|Cuy(Q)8w}gq^(A}BYJScEMlobsQ(5x#)?d+OTOm?S=1j*J8pKq- z$rxMxdpQ=IQkIaCtLUQE|LaUZYE0cu<*`_uk<6rdJ1`~4E^Y#kCodFLmGE&v3@%1O zSno85aXN{xru$c0KU_3%sxaxtt6>Hb0pPctH@cdPH$5fTuwCm@z({=lod+t^FmLrm z`p4KtqHMHn0CDAI4Ko@NX&Xx!TSUxnyZt6CZ&f1;HadT41@e`LS_Hc3AR&eNN5Y>+ zrEVuOy*7$JZd!}JTc%dx$~96>C# zxG|>un`+F7nh>^r*$<60DMQhM%+oZ1tu;%3%!ed^1S8D?Up1uKbIayP3o#TmHW(?c z9^a(z7C8I@T_((DHvV30f9|df81{4x)#SiK2VwDiyR_mw2iYyl;WQC)R0f-#kdYGn zG&Jx$C2BBk;Mf+@{v5bWz#>PM8hfRk|N2Go4fw#5IdVE=FjE<}mQYB=e5}!Z2e=&v z^p#pL+dH#oq`)(M5nPE~Sp=V%|62THzBhP*`V9Z=_l{lNo6G~WZQOT}4ZxQ9wf?r)%11xo)gJ$abC#c4tidX~Gqbz2#SG4iy>j$iSa`w25M3W3|q~0t?mnd*4f# zzadw?{HQE;TKVsS-u?X#!{5kgF&BiP|NiTg-!m?aA2F8J5f{C3PHgT!jFzKwJUYym z)rI+y(Gi`1s?ZC!`IO)_38Kh3=czeV`bBiSq`f^(v9C(% zE=6VWV8xSuR>JOT2q4+9B4wKp@OnvlZ~dwvm}3%>aK>uE+&wRVkk$=6w8;^D|QP&L&Fj<2vU*>&S$4I1M46&KWGt8Yxc zQwTfoWqrJABk!#%*7OO^n?+4gN> z)7uno_v_~E#RmHvMoXIHuR=6_fe@34DeYi24semRgi_mfT?qEAR1R*Am#TJHI7=P; z4y>sA0uJSO1?e@-Rx!gFYxU57BYI$|v;+;}OH;=iU|V2~)cBzp*%y(>U}W`;;2w}l zcChNf9_UPA6vce4K%(Xe=9!N%DsMUa^m^8B&SrZ!(Io8>_(WeLSV5px;wJc@OQ<4A z62h`8p@4E7I6b|t1t3p)YaiL%n6PCr(;{Q*X!kUB&sx-wG5Y!(I%;yPikeE(eDU#0 zO4k$c8A6W`6B(?^=r)^=A*w!bBMk>PggUxf_!{KR8=r@_jT7Ss16|G8RcY#|r%9auo z7QW8bT)$wR?CZ#bEt*`1tr3&1`a-X0JYW8F>uaaX-5i?`4@=SH9qkvZbsEUCvd@$_ z{$U}J2|GP9qc+o^+T#zGSvX>|ZfpPO4;UM~Z0ci++?L9h|eJ zvUPgUx)<7-0th3~$#8>y-au$-?_{Xil>y2+`KAo~u^R%lCcZcCFq3;Uy3mrMo@c`{ z##j=n#I#WZY2qCq!f2y~{V4+eyG7f;xs2jCPFd@2t%B%Z0B2Q*VwYDENhU70+hw$9~M)G_xJBV5UkjSsx;w( zwXTgc;7x9!XhzBF;^<#{e7=ipg5qY{jUI)bB z@$5!(fqC%-Vxk!lg$KtVc5x}~{)z3&u8;CcMJz1HjU>(e_%2>YJkJZ<^n8CzwfD8` zhErwJ*RPZWy$qL?oO(Kni1?%GL)0fW2rcw8 z%u8N++mLK=v#DAx3UN}S{1*)R^h;#{t%rljQbr-?_rR%X24c|npLmSui!qvYkulud zwf`dyx{rgApy+}S;6O`l{^39;OuNrd0K?xWQ}z8uU#qWvkPbBFXvs^fq! z_|yhdZyYfsmi}8|EZubN4S7D^!ofId2vHn(Fve!yfOu`W$9?Fswr{9ohvChxE&mkb z%$*;TC7K%E+O_VD>5KO;Q_1F{VsHKj)S%TsN=R3~sd%I0%Zb0aYA_;6@=wq0Xg`%k zIW@OOaCu(tqU=fEKYZ>125%%OX~AGdv^42+mDe|v!1KUr~1Z>E_ zj4+b;x5KoJdD?vl^n%6OH8#0n6|TkkB0u}#(!|DB*n#Lqhl|qiOB?BIeY-{2)QMPE zsq}PHm>CD2I#$(>Y@*Yhm9yW*`9z}wEMr(^bM2izM>SGAoXmHry+K#xRFb9)nium> zN@YB1Jbc+g8s)8ME^L8PrNmuV!_%j?2;uve{yn+jIZ+O0;Lb)A%lPskl8d#1LJ8z* zh`eX}e%DQTDtvIxSUctE(Zo$dLe8jqyqc6U(u<=2Q8>^8V@4h^hN@|_z*k0H>*H0Z zBR(NG00Eh(h)5II`KoHQ>{jl~$+yQ2HN}~&!R0+cgL%%VTniY6m=RE+yg77@hK1}UW0g*z@2ZwxYY$9I1C2EWDw zrlYhd-;o2kK3C-Lp`sI5DcDz4zGE%H-27zS2=V+AfoXyw%d0bvfCG%fS6lsIAU_Ek zGI-FctS9cS`o%gqU>=Li(3F}lyYYmwMA^TY%oSuZ z4JjW|jqa-)qRJ_jUK!jvf=rLAwhX`2)Y}tXrP$JC0K^3l?)CcvIqoRV#w||4K zmDEZdF_>2p0yI+bVp?v=ce+xgR756YsbzZEY!YY&wz!?5@S3KZ?0yQ_Em6$RD6%WKeToA(W`dwIG=@I5UL7v#+0&hk&3a(vQ}?o z6GgqcE-7+%814vRJcdPuxf0*o|9o@V^0@1zLL)KLm97u>bzGiRhEw?I8N?B!98`ZM zJhm>eW(itkiJLN{mvzc^YM?+kqMClUxZzQ-%mcd0mVm{vq}AT693!=MWE_ih8r8=O zbl6HOf)BOrgS_vzNJ}D!w<)-gQxI(oIU8+v*DG$%S*7{ zkq>n)#tq@6Hwu|$x_cPQ{03z|wCb`+&%Oyv9;Ex}_z}_{SOGVA9JR>I@5bqu2CVf3 z4^X^Q(B68Vt6z@!>fNDvO|^?^Dj~~7io*t#UmFccPU+jFy*HtC12EI_^uP@@T>X%- zm_SJ#B>Bt4{`sqxc=BB`(p1D4N5YC&(_8)2bBX28pyErzvbp1iBu8=e$>?^fcxRev zpYyt<&+y}yZ;k-&%=#}f!mZ3#%D$h$RMA}ckP8O#Dox}C$v|K|)X1@G=k+j}LZ(9W zK<)uV>O-^Tbw9++nXHuyx<0c-Uz+K8%Qxe+IWoym)-7XtDO~%MqQq5eM`j@Z|2tng zZv8I?LbVaLX(lW{J#Y?(&x8X$9JAqO1z;QaFVG7{<1@9PbES z1E>Axz#ZDbb;KoDzSiNO2n7)1SIjKSW-*_5JnSqbqxF%4OWFP_i&sPZt0|?Ds;&un z4MOITy<>A%6pbePWR-@{e6}~^3w@hmSm!P)70w}8UYMJigvQ4Im`1NMTFwt^5n(eH}isrw5hH_9McFzArlX)-&#wxdgaazfPoerl1c3b%^HKrJ@dAM zM=zS_R%^soo8nX~QiS94Uf6>IS8XDP@U~ZD{#ztd?j0c(fQgiwjG+^w%%;xYyj6&G zb~-dY1hM$l{K0|S8)`-^lbMS`(D(E6Lm4qyYUS~@xc?~TQ-!_nS;!)<}phpepR9S_p2 zGXqND{Oq`9_Y~f>Yc)yW)|nZX`@vG|a1dHr4ietJ#?^b9b7F>l=FT^#`(CTbRS_}~ z5AmoW1(?`Qwwr52n>lU}R7wbxYQ9V*QsiS4tdE_h@WuA88LFvNI97T%cho1V$2*w~ z2S`uR4F@>#^%bDYg;HXlTd*vwwI{L@?8ej%2GqW|sPMcqd$;ZgF&?W;@wJB8v-FU$ zq~ki&AKRYXv+=r?agyL@iKB6Ubo;iy0lG~h>u8niCwTQb$s?_}F883@E!j-SP~MyQ zi$x1fWI(J%PU$pbcO(XxxtU@vT%%>I2Ny2OJeL=7Ml(q?LEd%ZSx?j9Yr@BpQY7H* zPtvuNitV9rcmyQOazk!h)PhdY$rV+`mTW#a&ci*06}UDb#=H|#vLJ;}7E<1+_6Nj+ z@e@tU<@AQIYR51~FGBI4lJPd7i~P#IRHg443X&XHULU-BO0(Du+Qkk)V&NBMU^kbS zvstcpS^t;r@3USL%4OC^kDKNyvBgSFOx=RKE+tMQulB*+K}lp= zvNUJXPeAHD4{etv4b!cB?&T0`n5InxdA9?# zNSsPSW+szj8I|Vp3`O*{M8w3om-(;^ku9bE)h@Mb3yf~^Rg`vrqFJcZNERFzn#9$0 zeuuHnpUog~>QYiyX*eCEMNBG=(4G6^4ZM46B$HR>>?OIP2oz7zT(L;(NO>5Foja1g z=6f7&Ao$kP=Uv>DzuU2XRZpdLWPI9NM3*(k7bM>l^35!Q)s#bv8y@%i^(zQq6JKTU z+C9a!Z;mPWQ~N#yxak!@(>C`s`DmKRU2nO9{E820SM=3zlMGtGhpmeA%*_|0egoh5Xy;UTsi zc7oJF5$7{`wps082wv%a^Lo=xooF47gG(I8x9_JP(eUy8Ka-E?H}-#pz%SNbMj-gy z6FeU>B=YqLcs#_sGymz3`rG?jUeD!zeF$XH5^hNHQ2`+BIk2M-z`mDNWjy^8Fny(} zsk#iT+P9|)6zt5lW%1L@b|DQns{MK1$I!znGR9y`LxMqAr)6;eJiesZDi471?cM?c zxnt?P-p7mVQOU#{d(FNHusIGC$i)s@F);tkV?xsgJSDJa;}R~v7%4O;pcXoCJ}v}# ziG~>?khKB(-ywM+ptTD5dD=s^EMac8Av8fn34nlLRC-WHc-k${cumr{@I1QN9=+IK z8``NSYlz-5$ZFB5ioPYj!sqqEz4PaBJ>OItbdKv3dVR|kkjZGOGeQ12vg!r`Ci~Up zwA1~B~i3y!js=HA>vOSGbjG%OL3aZbkYrn`p zJb(VwrU%O*X;d+t`4V4zxG!-(-;$qbXqcHfu`?}Q(mb>((dMjLFlVh=;eu2S@2QY+ zO?>QZV|A{$*joU=)-Q2QiPfRFaQrJ|aq6)eA_PMm&C=`pLepEfkAywv!BfkwBf#Yo z-vl#b4e)qXj%7tKVfd}-jIRi}AXx-BqxOgs?9x5^4%`uvbeqcI9|}NHf!B;~7 zkr)P^dv~(DkjksR%C!sP#8b@@qnAfxwk+zA#*I$x(!=d$J?SOrScOfi6RcBPc?P_a zks6{1fEFHO?($<6=Dr;@^o?>({w^d{Gj*o85JXtlsmyH=7T3{?HH#jb1Z88zmkE3J94Dr2UQ03K+@2)i%%J&;kF#l4%q zd<@jw1n7OXQXh{lvnB~7g30((*~^e8D%E;&E-ozvI_3L?RxcCxA}M$OCymGDw-F3f zc`ZiN^q8+b6*3AzVVD5-r}Y1ld}GC`X+I-jGJ@>w<@?{b9-RTbYJ^YB5$OY$b!uyS z#HqH+ly8_}`ee@XfBgF2%&j~rkQGf#MA!%VA>SBJXSRW};w}2PBWPbDTK7Z0YG2RZ zxZij~1#JtJpFW(-t8M)fL}Z>dn=i+=;!wy&M7Jgt#I_>pa^4g(jAsF&X0@`pw#!74 zaHS!aD{4YFl!sARWBVS%gV$&kZlCcouh_AAnOBHFG>VDKmE3P`F0|0}{otRayMMjJ zZtqg)fnI;fu$}>!v)=`Qfc)QxLDYE~{&bNQkndz7%w`KT<^E+}K30jw=ICD?X?jD1 zL;xj4cXo}86`3xh^MG(D>c>nl6Abi~{*?e+3HP|)Hj#NqqO2!vP`Ahl`Yp(C(c+}Y zJFmLMYGDTnK9g?Q_o}Wh{yLhBU;2i<-`@vC#`u92mf-&!A2a1g(U!6L?o<@+A^hiV${^bAi8Ay}NFIOT#ET}fc3%3aF z-)5xLt?c5&MkmC1^aWS!#2(1NnNwrCk6(|f$<;Xof88^a5o7X8kHj^~$7*zK&zEww zCx4W=B~1ucY~S_JD&fz5riyCtvKf=qHpt0&-AX|6Spi~HXz|X@FD_4&Bo|tuI+B29 zc|glTFk7RRxYZI(FP+#3$sj9BwBxEtaoYy55T>^H_oG0bIZ=a?TLfn1$Mm#owL*fC z0Qu|@pSoNfUbutgA%3wx**^)-zx z&tYVgE{fVbG69$IKPYJmX`kYF867@A5lx_jcr$%Rer$TUFmqNyTVrCK^!XoFC^5)M$K-C~evNR4oS53wm{8KG zLRXfG{_7r;9!K_A*XLCF1Mn+JR14ujZ_P&TX5{(y$;@OV<*kXQ#HNMmR`iajbj($+ zZNI)j<3UN#i$2km-9)7gONF_Q7!`cuSM0&7B@rxN&BzzY$=FWF4$0$)9y`k;Ly)Dj zrKs}B&R9$d>i-yHchCG9bxVm@vzF2;VB13=;fh&Z}#SPygp@CyhPUbvIwb?klZp$}cW2C;AIjC``jf&IPL zEU>>P^~5Wnlj2QE0OPo6zis^MTUT+sVwRliretv)c$O{( zB`TNh(u!&}hrBnANF>^$DUW;giX94SL?*er)Iw>Z=(z)lE`2&!9vx^?7J`RzB1_XA zoCaOLqwy%`qyaCGzh^`g#bgSU7OolNG>ak5c!`3?mYc&&^u%eE#5wJE_FXYIhbG07 zUX_~5!#KizUWxFcZEU?}Kk?cG?V3~}yhOnv?n(8xKg`x1);h1fC}{P`rnpZb3PE;7 zj+VvvK7V?Xa|0el3Ap9&1D|+YE9;HTvd%q5H$uE{J_{;S;*s`zb~DasCbP8@h_GDP z*vW2LoHP)QAjOD^4nL{?JV(FGks4jy3AeA4b;#DI&{8B9Eln$IbU%PcPOQ6yqhMgv zKI-_@PweNLjkWS|tK@hrb2GE-!}hbB#XH5A()E-abnqb~LRj7DPNwCiZt-?yvCwZp zO0J87{^F_IGRE&D>I&yr@)v%gQHd=2TOT=d%&X6eNt~z03YqPFdd<`N3&@Qrme%a& z$-kjbw1wvI6(|xzm$507V9A3*9)pWNi%+N-K**0#D}2iFrlFx6OQSdwdX`Q#OW|H% z^K|1M`?-^p&ys$@rcX17V__bSI}|DADEKIn8WqR(Z#Bzx$5*B=-7I)oZ+&>FZb^PP z%23&(gU$1&oKg+RS46C!7dTkhv-<0qfaQPv8-QD2o*p&kQl2>DvE_2o#G!;EM?)ba zgnZEthgHp6oaZ^(Lg0#`HvpPQB7E$xv&JU(U~qwcA_VBd%w3{5SE$fB@6|oDl{*5u zN+YWYATmmn4iW&8r2nk@smFXNU!iC8(2ViPLz3qvS&I%b7mC?uNAEj-P z;4723YZ`P0`Fyk^&kO6raJ@P3J{H9OS`6Ref2>}no0vQ5F({25l_Jsmu{}Q$#-o5F z5e`2u(<>kne05X#J)I{Cox7#+ahi#e1 zD(`FSr;)b19-ZGkW8O~$CpVlr?wFL+B;saqpckPVUJ`qp!*=)Rb<`I&I-H_-#4i{_E}IfkhK?nx5MzXJ6&ZR zQ1I4l9aG7t=V+$$JSOd=c>@c8`guc#Oya_kS^WOF!LJh}yV4DLJMFhiA<$mlL@)&e z_&qO~*6&$gEMC+p1KZX{-VkQN@cm{R$=5<{>?(!5%ka(5C9z%OYYIo9xa8~Uo2-LZ~D zm%f+ev*4!!_p?f#FnwASDr$!>7ej9;v`{EUBgwe~W3c7{$6E%OIg+0Ft+G{sJab(}A9ea`wI{Bw49E z5QTkmQN_zEmMk6L+0(&G?QuaFr*tBuhM+lVTt0lrf?hp)TqmJspp ziX$m$=H7+dCw-zQXcBQGKzFdq=Nv;8N zNnC=bVmT#KjHt!ozg+N*#yKsD%R=K z^Ai)Hpy@e}(5Wv21#Bb}c1Ko5jvG$~a4OEoIMGFYZ9JZ4re?|B#|=9=qc#wR$tw8S zc8eM1Q*jF10^Y~#1Z3ZC0lPt@zhh4lLR9+LApKSvnr7Z{t!o>q>lfc+E!3*R-|bJx zCCE?R949#A%}l}PI6`|-(-9-Gyx8h#_Q}a=GUdtU+>QP}@n~U`mofJp=632!j^kR1 z3#J?g(X!dLeugsRkscH6VTypIf+auiiSG*xgB9*D?Rc_7YTY#oDivgMIYQxtUur1W zxt91u`-K~pqV+rQRa#WgeW&v5;uFNQxPKXhGC5%4I~OyJq{W~J^Y9Fu0nNMYs&|2R62O-gq3^gX#Y;VqfJ=W4rTW9!8@UABSsCn>r(jlZi9YweZo0;u zOJ_is46+*eZm-+-KkA+~<5Q+kGQ4nzGjRQubR5od8?xU^v(2)ysO8f)jddc%yPc zlqasoaW=l0Ib>3vinH+pb!y3%V~nY2C{*v-Haymt>ONEXa#;Azypr!n_*b7&5?D$8 zyUxf~owB&odnWmKztTo{(7#n)OJjAoX#8+vOUk_BRWrQiN%%oA=d

>Y%AS1QsdT&1Nr`?tbp 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 + + + net5 + InProcess + Linux + ..\.. + + + + + + + diff --git a/demo/src/ConfigMapFileProviderSample/Controllers/ValuesController.cs b/demo/src/ConfigMapFileProviderSample/Controllers/ValuesController.cs new file mode 100644 index 0000000..270dd87 --- /dev/null +++ b/demo/src/ConfigMapFileProviderSample/Controllers/ValuesController.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace ConfigMapFileProviderSample.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class ValuesController : ControllerBase + { + private readonly ILogger logger; + + public ValuesController(ILogger logger) + { + this.logger = logger; + } + + // GET api/values + [HttpGet] + public ActionResult> Get() + { + logger.LogDebug("DBG log"); + logger.LogInformation("INF log"); + logger.LogWarning("WRN log"); + logger.LogError("ERR log"); + logger.LogCritical("CRI log"); + return new string[] { "value1", "value2" }; + } + + // GET api/values/5 + [HttpGet("{id}")] + public ActionResult Get(int id) + { + return "value"; + } + + // POST api/values + [HttpPost] + public void Post([FromBody] string value) + { + } + + // PUT api/values/5 + [HttpPut("{id}")] + public void Put(int id, [FromBody] string value) + { + } + + // DELETE api/values/5 + [HttpDelete("{id}")] + public void Delete(int id) + { + } + } +} diff --git a/demo/src/ConfigMapFileProviderSample/Dockerfile b/demo/src/ConfigMapFileProviderSample/Dockerfile new file mode 100644 index 0000000..7cf4344 --- /dev/null +++ b/demo/src/ConfigMapFileProviderSample/Dockerfile @@ -0,0 +1,19 @@ +FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base +WORKDIR /app +EXPOSE 80 + +FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build +WORKDIR /src +COPY ["src/ConfigMapFileProviderSample/ConfigMapFileProviderSample.csproj", "src/ConfigMapFileProviderSample/"] +RUN dotnet restore "src/ConfigMapFileProviderSample/ConfigMapFileProviderSample.csproj" +COPY . . +WORKDIR "/src/src/ConfigMapFileProviderSample" +RUN dotnet build "ConfigMapFileProviderSample.csproj" -c Release -o /app + +FROM build AS publish +RUN dotnet publish "ConfigMapFileProviderSample.csproj" -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "ConfigMapFileProviderSample.dll"] \ No newline at end of file diff --git a/demo/src/ConfigMapFileProviderSample/Program.cs b/demo/src/ConfigMapFileProviderSample/Program.cs new file mode 100644 index 0000000..c711b7d --- /dev/null +++ b/demo/src/ConfigMapFileProviderSample/Program.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Logging; + +namespace ConfigMapFileProviderSample +{ + public class Program + { + public static void Main(string[] args) + { + CreateWebHostBuilder(args).Build().Run(); + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .ConfigureAppConfiguration(c => + { + c.AddJsonFile(ConfigMapFileProvider.FromRelativePath("config"), + "appsettings.json", + optional: true, + reloadOnChange: true); + }) + .UseStartup(); + } +} diff --git a/demo/src/ConfigMapFileProviderSample/Startup.cs b/demo/src/ConfigMapFileProviderSample/Startup.cs new file mode 100644 index 0000000..550638b --- /dev/null +++ b/demo/src/ConfigMapFileProviderSample/Startup.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace ConfigMapFileProviderSample +{ + + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseMvc(); + } + } +} diff --git a/demo/src/ConfigMapFileProviderSample/appsettings.Development.json b/demo/src/ConfigMapFileProviderSample/appsettings.Development.json new file mode 100644 index 0000000..e203e94 --- /dev/null +++ b/demo/src/ConfigMapFileProviderSample/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/demo/src/ConfigMapFileProviderSample/appsettings.json b/demo/src/ConfigMapFileProviderSample/appsettings.json new file mode 100644 index 0000000..def9159 --- /dev/null +++ b/demo/src/ConfigMapFileProviderSample/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/demo/src/ConfigMapFileProviderSample/configmap.yaml b/demo/src/ConfigMapFileProviderSample/configmap.yaml new file mode 100644 index 0000000..ec587ae --- /dev/null +++ b/demo/src/ConfigMapFileProviderSample/configmap.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: demo-config +data: + appsettings.json: |- + { + "Logging": { + "LogLevel": { + "Default": "Error", + "System": "Error", + "Microsoft": "Error" + } + } + } \ No newline at end of file diff --git a/demo/src/ConfigMapFileProviderSample/deployment.yaml b/demo/src/ConfigMapFileProviderSample/deployment.yaml new file mode 100644 index 0000000..02d9072 --- /dev/null +++ b/demo/src/ConfigMapFileProviderSample/deployment.yaml @@ -0,0 +1,29 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: demo-deployment + labels: + app: config-demo-app +spec: + replicas: 1 + selector: + matchLabels: + app: config-demo-app + template: + metadata: + labels: + app: config-demo-app + spec: + containers: + - name: configmapfileprovidersample + imagePullPolicy: Always + image: fbeltrao/configmapfileprovidersample:1.0 + ports: + - containerPort: 80 + volumeMounts: + - name: config-volume + mountPath: /app/config + volumes: + - name: config-volume + configMap: + name: demo-config \ No newline at end of file diff --git a/src/ConfigMapFileProvider/ConfigMapFileProvider.csproj b/src/ConfigMapFileProvider/ConfigMapFileProvider.csproj index 939a0ac..5164c1c 100644 --- a/src/ConfigMapFileProvider/ConfigMapFileProvider.csproj +++ b/src/ConfigMapFileProvider/ConfigMapFileProvider.csproj @@ -1,23 +1,33 @@ - netcoreapp3.1 + netstandard2.0;netcoreapp3.1;net5 Dynamic reload config from file for Kubernetes with Config Map. Usage: - configBuilder.SetBasePath(Path.Join(AppContext.BaseDirectory, "config")) - .AddJsonFile(ConfigMapFileProvider.FromRelativePath("config"), "appsettings.json", true, true); + static IHostBuilder CreateHostBuilder(string[] args) + { + return Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args) + .ConfigureAppConfiguration((c,b)=>{ + b.SetBasePath(Path.Join(AppContext.BaseDirectory,"config")) + .AddJsonFile(ConfigMapFileProvider.FromRelativePath("config"), "appsettings.json"); + }) + .UseConsoleLifetime() + ; + } - 1.0.0 + 2.0.1 Jaine.ch Jaine.ch - First version + Config Map File Provider + MIT + Support .Net 5 - - - + + + From fff64b58782785939722360a796d6aa84a8e5bf3 Mon Sep 17 00:00:00 2001 From: "jaine.ch" Date: Thu, 12 Aug 2021 11:03:33 +0800 Subject: [PATCH 3/3] Disable the output in console about Checking for --- src/ConfigMapFileProvider/ConfigMapFileProvider.csproj | 2 +- src/ConfigMapFileProvider/ConfigMapFileProviderChangeToken.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ConfigMapFileProvider/ConfigMapFileProvider.csproj b/src/ConfigMapFileProvider/ConfigMapFileProvider.csproj index 5164c1c..0385793 100644 --- a/src/ConfigMapFileProvider/ConfigMapFileProvider.csproj +++ b/src/ConfigMapFileProvider/ConfigMapFileProvider.csproj @@ -21,7 +21,7 @@ Jaine.ch Config Map File Provider MIT - Support .Net 5 + Disable the output in console about "Checking for ..." diff --git a/src/ConfigMapFileProvider/ConfigMapFileProviderChangeToken.cs b/src/ConfigMapFileProvider/ConfigMapFileProviderChangeToken.cs index d11286d..58f7f20 100644 --- a/src/ConfigMapFileProvider/ConfigMapFileProviderChangeToken.cs +++ b/src/ConfigMapFileProvider/ConfigMapFileProviderChangeToken.cs @@ -85,7 +85,7 @@ private void CheckForChanges(object state) { var fullPath = Path.Combine(rootPath, filter); - Console.WriteLine($"Checking for changes in {fullPath}"); + //Console.WriteLine($"Checking for changes in {fullPath}"); var newCheckSum = GetFileChecksum(fullPath); var newHasChangesValue = false;