├── media
└── article-preview.png
├── src
└── ConfigMapFileProviderSample
│ ├── appsettings.json
│ ├── appsettings.Development.json
│ ├── configmap.yaml
│ ├── ConfigMapFileProviderSample.csproj
│ ├── deployment.yaml
│ ├── Dockerfile
│ ├── Program.cs
│ ├── Startup.cs
│ ├── Controllers
│ └── ValuesController.cs
│ ├── ConfigMapFileProvider.cs
│ └── ConfigMapFileProviderChangeToken.cs
├── .dockerignore
├── LICENSE
├── ConfigMapFileProviderSample.sln
├── .gitignore
└── README.md
/media/article-preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fbeltrao/ConfigMapFileProvider/HEAD/media/article-preview.png
--------------------------------------------------------------------------------
/src/ConfigMapFileProviderSample/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Warning"
5 | }
6 | },
7 | "AllowedHosts": "*"
8 | }
9 |
--------------------------------------------------------------------------------
/src/ConfigMapFileProviderSample/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/ConfigMapFileProviderSample/configmap.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: demo-config
5 | data:
6 | appsettings.json: |-
7 | {
8 | "Logging": {
9 | "LogLevel": {
10 | "Default": "Error",
11 | "System": "Error",
12 | "Microsoft": "Error"
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/.dockerignore
2 | **/.env
3 | **/.git
4 | **/.gitignore
5 | **/.vs
6 | **/.vscode
7 | **/*.*proj.user
8 | **/azds.yaml
9 | **/charts
10 | **/bin
11 | **/obj
12 | **/Dockerfile
13 | **/Dockerfile.develop
14 | **/docker-compose.yml
15 | **/docker-compose.*.yml
16 | **/*.dbmdl
17 | **/*.jfm
18 | **/secrets.dev.yaml
19 | **/values.dev.yaml
20 | **/.toolstarget
--------------------------------------------------------------------------------
/src/ConfigMapFileProviderSample/ConfigMapFileProviderSample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.2
5 | InProcess
6 | Linux
7 | ..\..
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/ConfigMapFileProviderSample/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: demo-deployment
5 | labels:
6 | app: config-demo-app
7 | spec:
8 | replicas: 1
9 | selector:
10 | matchLabels:
11 | app: config-demo-app
12 | template:
13 | metadata:
14 | labels:
15 | app: config-demo-app
16 | spec:
17 | containers:
18 | - name: configmapfileprovidersample
19 | imagePullPolicy: Always
20 | image: fbeltrao/configmapfileprovidersample:1.0
21 | ports:
22 | - containerPort: 80
23 | volumeMounts:
24 | - name: config-volume
25 | mountPath: /app/config
26 | volumes:
27 | - name: config-volume
28 | configMap:
29 | name: demo-config
--------------------------------------------------------------------------------
/src/ConfigMapFileProviderSample/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base
2 | WORKDIR /app
3 | EXPOSE 80
4 |
5 | FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build
6 | WORKDIR /src
7 | COPY ["src/ConfigMapFileProviderSample/ConfigMapFileProviderSample.csproj", "src/ConfigMapFileProviderSample/"]
8 | RUN dotnet restore "src/ConfigMapFileProviderSample/ConfigMapFileProviderSample.csproj"
9 | COPY . .
10 | WORKDIR "/src/src/ConfigMapFileProviderSample"
11 | RUN dotnet build "ConfigMapFileProviderSample.csproj" -c Release -o /app
12 |
13 | FROM build AS publish
14 | RUN dotnet publish "ConfigMapFileProviderSample.csproj" -c Release -o /app
15 |
16 | FROM base AS final
17 | WORKDIR /app
18 | COPY --from=publish /app .
19 | ENTRYPOINT ["dotnet", "ConfigMapFileProviderSample.dll"]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Francisco Beltrao
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/ConfigMapFileProviderSample/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Threading.Tasks;
7 | using Microsoft.AspNetCore;
8 | using Microsoft.AspNetCore.Hosting;
9 | using Microsoft.Extensions.Configuration;
10 | using Microsoft.Extensions.FileProviders;
11 | using Microsoft.Extensions.Logging;
12 |
13 | namespace ConfigMapFileProviderSample
14 | {
15 | public class Program
16 | {
17 | public static void Main(string[] args)
18 | {
19 | CreateWebHostBuilder(args).Build().Run();
20 | }
21 |
22 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
23 | WebHost.CreateDefaultBuilder(args)
24 | .ConfigureAppConfiguration(c =>
25 | {
26 | c.AddJsonFile(ConfigMapFileProvider.FromRelativePath("config"),
27 | "appsettings.json",
28 | optional: true,
29 | reloadOnChange: true);
30 | })
31 | .UseStartup();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/ConfigMapFileProviderSample.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29215.179
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConfigMapFileProviderSample", "src\ConfigMapFileProviderSample\ConfigMapFileProviderSample.csproj", "{1343A1BF-33E0-49D5-AADB-A323867697AE}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {1343A1BF-33E0-49D5-AADB-A323867697AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {1343A1BF-33E0-49D5-AADB-A323867697AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {1343A1BF-33E0-49D5-AADB-A323867697AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {1343A1BF-33E0-49D5-AADB-A323867697AE}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {AB82D515-15E8-4AAE-A7B7-0CDC78DB7234}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/src/ConfigMapFileProviderSample/Startup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Builder;
6 | using Microsoft.AspNetCore.Hosting;
7 | using Microsoft.AspNetCore.Mvc;
8 | using Microsoft.Extensions.Configuration;
9 | using Microsoft.Extensions.DependencyInjection;
10 | using Microsoft.Extensions.Logging;
11 | using Microsoft.Extensions.Options;
12 |
13 | namespace ConfigMapFileProviderSample
14 | {
15 |
16 | public class Startup
17 | {
18 | public Startup(IConfiguration configuration)
19 | {
20 | Configuration = configuration;
21 | }
22 |
23 | public IConfiguration Configuration { get; }
24 |
25 | // This method gets called by the runtime. Use this method to add services to the container.
26 | public void ConfigureServices(IServiceCollection services)
27 | {
28 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
29 | }
30 |
31 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
32 | public void Configure(IApplicationBuilder app, IHostingEnvironment env)
33 | {
34 | if (env.IsDevelopment())
35 | {
36 | app.UseDeveloperExceptionPage();
37 | }
38 |
39 | app.UseMvc();
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/ConfigMapFileProviderSample/Controllers/ValuesController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Mvc;
6 | using Microsoft.Extensions.Logging;
7 |
8 | namespace ConfigMapFileProviderSample.Controllers
9 | {
10 | [Route("api/[controller]")]
11 | [ApiController]
12 | public class ValuesController : ControllerBase
13 | {
14 | private readonly ILogger logger;
15 |
16 | public ValuesController(ILogger logger)
17 | {
18 | this.logger = logger;
19 | }
20 |
21 | // GET api/values
22 | [HttpGet]
23 | public ActionResult> Get()
24 | {
25 | logger.LogDebug("DBG log");
26 | logger.LogInformation("INF log");
27 | logger.LogWarning("WRN log");
28 | logger.LogError("ERR log");
29 | logger.LogCritical("CRI log");
30 | return new string[] { "value1", "value2" };
31 | }
32 |
33 | // GET api/values/5
34 | [HttpGet("{id}")]
35 | public ActionResult Get(int id)
36 | {
37 | return "value";
38 | }
39 |
40 | // POST api/values
41 | [HttpPost]
42 | public void Post([FromBody] string value)
43 | {
44 | }
45 |
46 | // PUT api/values/5
47 | [HttpPut("{id}")]
48 | public void Put(int id, [FromBody] string value)
49 | {
50 | }
51 |
52 | // DELETE api/values/5
53 | [HttpDelete("{id}")]
54 | public void Delete(int id)
55 | {
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/ConfigMapFileProviderSample/ConfigMapFileProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.FileProviders;
2 | using Microsoft.Extensions.FileProviders.Internal;
3 | using Microsoft.Extensions.FileProviders.Physical;
4 | using Microsoft.Extensions.Primitives;
5 | using System.Collections.Concurrent;
6 | using System.IO;
7 | using System.Reflection;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 |
11 | namespace ConfigMapFileProviderSample
12 | {
13 | ///
14 | /// Simple implementation using config maps as source
15 | /// Config maps volumes in Linux/Kubernetes are implemented as symlink files.
16 | /// Once reloaded their Last modified date does not change. This implementation uses a check sum to verify
17 | ///
18 | public class ConfigMapFileProvider : IFileProvider
19 | {
20 | ConcurrentDictionary watchers;
21 |
22 | public static IFileProvider FromRelativePath(string subPath)
23 | {
24 | var executableLocation = Assembly.GetEntryAssembly().Location;
25 | var executablePath = Path.GetDirectoryName(executableLocation);
26 | var configPath = Path.Combine(executablePath, subPath);
27 | if (Directory.Exists(configPath))
28 | {
29 | return new ConfigMapFileProvider(configPath);
30 | }
31 |
32 | return null;
33 | }
34 |
35 | public ConfigMapFileProvider(string rootPath)
36 | {
37 | if (string.IsNullOrWhiteSpace(rootPath))
38 | {
39 | throw new System.ArgumentException("Invalid root path", nameof(rootPath));
40 | }
41 |
42 | RootPath = rootPath;
43 | watchers = new ConcurrentDictionary();
44 | }
45 |
46 | public string RootPath { get; }
47 |
48 | public IDirectoryContents GetDirectoryContents(string subpath)
49 | {
50 | return new PhysicalDirectoryContents(Path.Combine(RootPath, subpath));
51 | }
52 |
53 | public IFileInfo GetFileInfo(string subpath)
54 | {
55 | var fi = new FileInfo(Path.Combine(RootPath, subpath));
56 | return new PhysicalFileInfo(fi);
57 | }
58 |
59 | public IChangeToken Watch(string filter)
60 | {
61 | var watcher = watchers.AddOrUpdate(filter,
62 | addValueFactory: (f) =>
63 | {
64 | return new ConfigMapFileProviderChangeToken(RootPath, filter);
65 | },
66 | updateValueFactory: (f, e) =>
67 | {
68 | e.Dispose();
69 | return new ConfigMapFileProviderChangeToken(RootPath, filter);
70 | });
71 |
72 | watcher.EnsureStarted();
73 | return watcher;
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/ConfigMapFileProviderSample/ConfigMapFileProviderChangeToken.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Primitives;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Security.Cryptography;
6 | using System.Threading;
7 | using Timer = System.Threading.Timer;
8 |
9 | namespace ConfigMapFileProviderSample
10 | {
11 | public sealed class ConfigMapFileProviderChangeToken : IChangeToken, IDisposable
12 | {
13 | class CallbackRegistration : IDisposable
14 | {
15 | Action