Skip to content

Commit aa14f62

Browse files
ocdibuehler
authored andcommitted
feat: Introduce a Generic Host method for adding Kubernetes controllers (#44)
1 parent 81f099b commit aa14f62

28 files changed

+352
-132
lines changed

DotnetOperatorSdk.sln

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,28 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.30406.217
5+
MinimumVisualStudioVersion = 10.0.40219.1
36
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{95F3A6DD-B421-441D-B263-1B34A1465FF5}"
47
ProjectSection(SolutionItems) = preProject
58
README.md = README.md
69
EndProjectSection
710
EndProject
8-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KubeOps", "src\KubeOps\KubeOps.csproj", "{D7AB6CB9-94B6-4FEB-B7D8-D8AA793BD2A4}"
11+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KubeOps", "src\KubeOps\KubeOps.csproj", "{D7AB6CB9-94B6-4FEB-B7D8-D8AA793BD2A4}"
912
EndProject
1013
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{50E9B964-68F7-4B9F-BEA8-165CE45BC5C6}"
1114
EndProject
12-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KubeOps.Test", "tests\KubeOps.Test\KubeOps.Test.csproj", "{A33D30D0-AC1B-48F8-8A5A-36E569981793}"
15+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KubeOps.Test", "tests\KubeOps.Test\KubeOps.Test.csproj", "{A33D30D0-AC1B-48F8-8A5A-36E569981793}"
1316
EndProject
1417
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "build\_build.csproj", "{D47717CB-A02E-4B12-BAA8-1D7F8BAE9BBD}"
1518
EndProject
1619
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{9AF95FE4-DA1F-4BB0-B60E-23FCFA6AAAA2}"
1720
EndProject
18-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KubeOps.TestOperator", "tests\KubeOps.TestOperator\KubeOps.TestOperator.csproj", "{751BDC14-D75F-4DDE-9C45-2432041FBCAC}"
21+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KubeOps.TestOperator", "tests\KubeOps.TestOperator\KubeOps.TestOperator.csproj", "{751BDC14-D75F-4DDE-9C45-2432041FBCAC}"
1922
EndProject
20-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KubeOps.TestOperator.Test", "tests\KubeOps.TestOperator.Test\KubeOps.TestOperator.Test.csproj", "{B374D7E4-E9BA-47F8-B1A4-440DECD376E4}"
23+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KubeOps.TestOperator.Test", "tests\KubeOps.TestOperator.Test\KubeOps.TestOperator.Test.csproj", "{B374D7E4-E9BA-47F8-B1A4-440DECD376E4}"
24+
EndProject
25+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KubeOps.GenericTest", "tests\KubeOps.GenericTest\KubeOps.GenericTest.csproj", "{714D8DD8-AF4B-4990-AE63-0258BC3E3CD3}"
2126
EndProject
2227
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{EF94C48A-1D2A-459B-B397-A130463FF52D}"
2328
ProjectSection(SolutionItems) = preProject
@@ -35,8 +40,6 @@ Global
3540
Release|Any CPU = Release|Any CPU
3641
EndGlobalSection
3742
GlobalSection(ProjectConfigurationPlatforms) = postSolution
38-
{D47717CB-A02E-4B12-BAA8-1D7F8BAE9BBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39-
{D47717CB-A02E-4B12-BAA8-1D7F8BAE9BBD}.Release|Any CPU.ActiveCfg = Release|Any CPU
4043
{D7AB6CB9-94B6-4FEB-B7D8-D8AA793BD2A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
4144
{D7AB6CB9-94B6-4FEB-B7D8-D8AA793BD2A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
4245
{D7AB6CB9-94B6-4FEB-B7D8-D8AA793BD2A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -45,6 +48,8 @@ Global
4548
{A33D30D0-AC1B-48F8-8A5A-36E569981793}.Debug|Any CPU.Build.0 = Debug|Any CPU
4649
{A33D30D0-AC1B-48F8-8A5A-36E569981793}.Release|Any CPU.ActiveCfg = Release|Any CPU
4750
{A33D30D0-AC1B-48F8-8A5A-36E569981793}.Release|Any CPU.Build.0 = Release|Any CPU
51+
{D47717CB-A02E-4B12-BAA8-1D7F8BAE9BBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
52+
{D47717CB-A02E-4B12-BAA8-1D7F8BAE9BBD}.Release|Any CPU.ActiveCfg = Release|Any CPU
4853
{751BDC14-D75F-4DDE-9C45-2432041FBCAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
4954
{751BDC14-D75F-4DDE-9C45-2432041FBCAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
5055
{751BDC14-D75F-4DDE-9C45-2432041FBCAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -53,12 +58,23 @@ Global
5358
{B374D7E4-E9BA-47F8-B1A4-440DECD376E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
5459
{B374D7E4-E9BA-47F8-B1A4-440DECD376E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
5560
{B374D7E4-E9BA-47F8-B1A4-440DECD376E4}.Release|Any CPU.Build.0 = Release|Any CPU
61+
{714D8DD8-AF4B-4990-AE63-0258BC3E3CD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
62+
{714D8DD8-AF4B-4990-AE63-0258BC3E3CD3}.Debug|Any CPU.Build.0 = Debug|Any CPU
63+
{714D8DD8-AF4B-4990-AE63-0258BC3E3CD3}.Release|Any CPU.ActiveCfg = Release|Any CPU
64+
{714D8DD8-AF4B-4990-AE63-0258BC3E3CD3}.Release|Any CPU.Build.0 = Release|Any CPU
65+
EndGlobalSection
66+
GlobalSection(SolutionProperties) = preSolution
67+
HideSolutionNode = FALSE
5668
EndGlobalSection
5769
GlobalSection(NestedProjects) = preSolution
5870
{D7AB6CB9-94B6-4FEB-B7D8-D8AA793BD2A4} = {95F3A6DD-B421-441D-B263-1B34A1465FF5}
5971
{A33D30D0-AC1B-48F8-8A5A-36E569981793} = {50E9B964-68F7-4B9F-BEA8-165CE45BC5C6}
6072
{D47717CB-A02E-4B12-BAA8-1D7F8BAE9BBD} = {9AF95FE4-DA1F-4BB0-B60E-23FCFA6AAAA2}
6173
{751BDC14-D75F-4DDE-9C45-2432041FBCAC} = {50E9B964-68F7-4B9F-BEA8-165CE45BC5C6}
6274
{B374D7E4-E9BA-47F8-B1A4-440DECD376E4} = {50E9B964-68F7-4B9F-BEA8-165CE45BC5C6}
75+
{714D8DD8-AF4B-4990-AE63-0258BC3E3CD3} = {50E9B964-68F7-4B9F-BEA8-165CE45BC5C6}
76+
EndGlobalSection
77+
GlobalSection(ExtensibilityGlobals) = postSolution
78+
SolutionGuid = {ADAA5DFB-A7E5-490C-88EF-48D67C715F24}
6379
EndGlobalSection
6480
EndGlobal
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using k8s;
4+
using KubeOps.Operator.Caching;
5+
using KubeOps.Operator.Client;
6+
using KubeOps.Operator.Controller;
7+
using KubeOps.Operator.Queue;
8+
using KubeOps.Operator.Serialization;
9+
using KubeOps.Operator.Watcher;
10+
using Microsoft.Extensions.DependencyInjection;
11+
using Newtonsoft.Json;
12+
using Newtonsoft.Json.Converters;
13+
using Newtonsoft.Json.Serialization;
14+
using YamlDotNet.Serialization;
15+
16+
namespace KubeOps.Operator
17+
{
18+
public static class BuilderExtensions
19+
{
20+
public static IOperatorBuilder AddKubernetesOperator(this IServiceCollection services, Action<OperatorSettings> configure)
21+
{
22+
var settings = new OperatorSettings();
23+
configure(settings);
24+
return AddKubernetesOperator(services, settings);
25+
}
26+
27+
public static IOperatorBuilder AddKubernetesOperator(this IServiceCollection services, OperatorSettings settings)
28+
{
29+
services.AddSingleton(settings);
30+
31+
// support lazy service resolution
32+
services.AddTransient(typeof(Lazy<>), typeof(LazyService<>));
33+
34+
services.AddTransient(
35+
_ => new JsonSerializerSettings
36+
{
37+
ContractResolver = new NamingConvention(),
38+
Converters = new List<JsonConverter>
39+
{
40+
new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy() },
41+
},
42+
});
43+
44+
services.AddTransient(
45+
_ => new SerializerBuilder()
46+
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull)
47+
.WithNamingConvention(new NamingConvention())
48+
.Build());
49+
50+
services.AddTransient<EntitySerializer>();
51+
52+
services.AddTransient<IKubernetesClient, KubernetesClient>();
53+
services.AddSingleton<IKubernetes>(
54+
_ =>
55+
{
56+
var config = KubernetesClientConfiguration.BuildDefaultConfig();
57+
58+
return new Kubernetes(config, new ClientUrlFixer())
59+
{
60+
SerializationSettings =
61+
{
62+
ContractResolver = new NamingConvention(),
63+
Converters = new List<JsonConverter>
64+
{ new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy() } },
65+
},
66+
DeserializationSettings =
67+
{
68+
ContractResolver = new NamingConvention(),
69+
Converters = new List<JsonConverter>
70+
{ new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy() } },
71+
},
72+
};
73+
});
74+
75+
services.AddTransient(typeof(IResourceCache<>), typeof(ResourceCache<>));
76+
services.AddTransient(typeof(IResourceWatcher<>), typeof(ResourceWatcher<>));
77+
services.AddTransient(typeof(IResourceEventQueue<>), typeof(ResourceEventQueue<>));
78+
services.AddTransient(typeof(ResourceServices<>));
79+
80+
return new OperatorBuilder(services);
81+
}
82+
}
83+
}

src/KubeOps/Operator/Caching/CacheComparisonResult.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
namespace KubeOps.Operator.Caching
22
{
3-
internal enum CacheComparisonResult
3+
public enum CacheComparisonResult
44
{
55
New,
66
Modified,

src/KubeOps/Operator/Caching/IResourceCache.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
namespace KubeOps.Operator.Caching
55
{
6-
internal interface IResourceCache<TEntity>
6+
public interface IResourceCache<TEntity>
77
where TEntity : IKubernetesObject<V1ObjectMeta>
88
{
99
TEntity Get(string id);

src/KubeOps/Operator/Caching/ResourceCache.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ internal class ResourceCache<TEntity> : IResourceCache<TEntity>
2626

2727
private readonly IDictionary<string, TEntity> _cache = new ConcurrentDictionary<string, TEntity>();
2828

29-
private readonly ResourceCacheMetrics<TEntity> _metrics = new ResourceCacheMetrics<TEntity>();
29+
private readonly ResourceCacheMetrics<TEntity> _metrics;
30+
31+
public ResourceCache(OperatorSettings settings)
32+
{
33+
_metrics = new ResourceCacheMetrics<TEntity>(settings);
34+
}
3035

3136
public TEntity Get(string id) => _cache[id];
3237

src/KubeOps/Operator/Controller/ResourceControllerBase.cs

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using k8s;
77
using k8s.Models;
88
using KubeOps.Operator.Client;
9-
using KubeOps.Operator.DependencyInjection;
109
using KubeOps.Operator.Finalizer;
1110
using KubeOps.Operator.Queue;
1211
using Microsoft.Extensions.DependencyInjection;
@@ -28,29 +27,22 @@ public abstract class ResourceControllerBase<TEntity> : IResourceController<TEnt
2827

2928
private readonly ILogger<ResourceControllerBase<TEntity>> _logger;
3029
private readonly IResourceEventQueue<TEntity> _eventQueue;
30+
private readonly Lazy<IEnumerable<IResourceFinalizer<TEntity>>> _finalizers;
3131
private bool _running;
3232

33-
protected ResourceControllerBase()
34-
: this(
35-
DependencyInjector.Services.GetRequiredService<ILogger<ResourceControllerBase<TEntity>>>(),
36-
DependencyInjector.Services.GetRequiredService<IKubernetesClient>(),
37-
DependencyInjector.Services.GetRequiredService<IResourceEventQueue<TEntity>>())
33+
protected ResourceControllerBase(ResourceServices<TEntity> services)
3834
{
39-
}
40-
41-
protected ResourceControllerBase(
42-
ILogger<ResourceControllerBase<TEntity>> logger,
43-
IKubernetesClient client,
44-
IResourceEventQueue<TEntity> eventQueue)
45-
{
46-
_logger = logger;
47-
_eventQueue = eventQueue;
48-
Client = client;
35+
_logger = services.LoggerFactory.CreateLogger<ResourceControllerBase<TEntity>>();
36+
_eventQueue = services.EventQueue;
37+
_finalizers = services.Finalizers;
38+
Services = services;
4939
}
5040

5141
bool IResourceController.Running => _running;
5242

53-
protected IKubernetesClient Client { get; }
43+
protected IKubernetesClient Client => Services.Client;
44+
45+
protected ResourceServices<TEntity> Services { get; }
5446

5547
public async Task StartAsync(CancellationToken cancellationToken)
5648
{
@@ -135,9 +127,7 @@ private async void OnResourceEvent(object? _, (ResourceEventType Type, TEntity R
135127
return;
136128
}
137129

138-
var finalizer = DependencyInjector.Services
139-
.GetServices<IResourceFinalizer<TEntity>>()
140-
.FirstOrDefault(f => f.Identifier == resource.Metadata.Finalizers.First());
130+
var finalizer = _finalizers.Value.FirstOrDefault(f => f.Identifier == resource.Metadata.Finalizers.First());
141131
if (finalizer == null)
142132
{
143133
_logger.LogDebug(
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using k8s;
4+
using k8s.Models;
5+
using KubeOps.Operator.Caching;
6+
using KubeOps.Operator.Client;
7+
using KubeOps.Operator.Finalizer;
8+
using KubeOps.Operator.Queue;
9+
using KubeOps.Operator.Watcher;
10+
using Microsoft.Extensions.Logging;
11+
12+
namespace KubeOps.Operator.Controller
13+
{
14+
public class ResourceServices<TEntity>
15+
where TEntity : IKubernetesObject<V1ObjectMeta>
16+
{
17+
public ResourceServices(
18+
ILoggerFactory loggerFactory,
19+
IKubernetesClient client,
20+
IResourceCache<TEntity> resourceCache,
21+
IResourceEventQueue<TEntity> eventQueue,
22+
Lazy<IEnumerable<IResourceFinalizer<TEntity>>> finalizers,
23+
OperatorSettings settings)
24+
{
25+
LoggerFactory = loggerFactory;
26+
Client = client;
27+
ResourceCache = resourceCache;
28+
EventQueue = eventQueue;
29+
Finalizers = finalizers;
30+
Settings = settings;
31+
}
32+
33+
public ILoggerFactory LoggerFactory { get; }
34+
35+
public IKubernetesClient Client { get; }
36+
37+
public IResourceCache<TEntity> ResourceCache { get; }
38+
39+
public IResourceEventQueue<TEntity> EventQueue { get; }
40+
41+
public Lazy<IEnumerable<IResourceFinalizer<TEntity>>> Finalizers { get; }
42+
43+
public OperatorSettings Settings { get; }
44+
}
45+
}

src/KubeOps/Operator/DependencyInjection/DependencyInjector.cs

Lines changed: 0 additions & 10 deletions
This file was deleted.

src/KubeOps/Operator/DevOps/ResourceCacheMetrics.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
using k8s;
22
using k8s.Models;
3-
using KubeOps.Operator.DependencyInjection;
43
using KubeOps.Operator.Entities.Extensions;
5-
using Microsoft.Extensions.DependencyInjection;
64
using Prometheus;
75

86
namespace KubeOps.Operator.DevOps
@@ -19,9 +17,8 @@ internal class ResourceCacheMetrics<TEntity>
1917
"scope",
2018
};
2119

22-
public ResourceCacheMetrics()
20+
public ResourceCacheMetrics(OperatorSettings settings)
2321
{
24-
var settings = DependencyInjector.Services.GetRequiredService<OperatorSettings>();
2522
var crd = CustomEntityDefinitionExtensions.CreateResourceDefinition<TEntity>();
2623
var labelValues = new[]
2724
{

src/KubeOps/Operator/DevOps/ResourceEventQueueMetrics.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
using k8s;
22
using k8s.Models;
3-
using KubeOps.Operator.DependencyInjection;
43
using KubeOps.Operator.Entities.Extensions;
5-
using Microsoft.Extensions.DependencyInjection;
64
using Prometheus;
75

86
namespace KubeOps.Operator.DevOps
@@ -19,9 +17,8 @@ internal class ResourceEventQueueMetrics<TEntity>
1917
"scope",
2018
};
2119

22-
public ResourceEventQueueMetrics()
20+
public ResourceEventQueueMetrics(OperatorSettings settings)
2321
{
24-
var settings = DependencyInjector.Services.GetRequiredService<OperatorSettings>();
2522
var crd = CustomEntityDefinitionExtensions.CreateResourceDefinition<TEntity>();
2623
var labelValues = new[]
2724
{

0 commit comments

Comments
 (0)