Skip to content

Commit 6a33e06

Browse files
authored
Some unit Tests and Refactoring some Commands (#14)
1 parent 40b0034 commit 6a33e06

37 files changed

+1555
-130
lines changed

examples/SimpleOperator/Program.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
using SimpleOperator.Projects;
55

66

7-
var builder = OperatorHost.CreateOperatorApplicationBuilder(args);
7+
var builder = OperatorHost.CreateOperatorApplicationBuilder(args)
8+
.WithName("simple-operator");
89

910
builder.AddController<TestItemController>()
1011
.WithFinalizer("testitem.local.finalizer");
@@ -14,10 +15,6 @@
1415

1516
var app = builder.Build();
1617

17-
var writer = new StringWriter();
18-
19-
await new Install(app, writer).RunAsync();
20-
21-
Console.WriteLine(writer.ToString());
18+
app.AddInstall();
2219

2320
await app.RunAsync();

examples/SimpleOperator/Properties/launchSettings.json

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,35 @@
22
"profiles": {
33
"Operator": {
44
"commandName": "Project",
5-
"commandLineArgs": "--operator",
5+
"commandLineArgs": "operator",
66
"environmentVariables": {
77
"ASPNETCORE_ENVIRONMENT": "Development"
88
},
99
"dotnetRunMessages": true
1010
},
1111
"Install": {
1212
"commandName": "Project",
13-
"commandLineArgs": "install=true --export true",
13+
"commandLineArgs": "install",
1414
"environmentVariables": {
1515
"ASPNETCORE_ENVIRONMENT": "Development"
1616
},
1717
"dotnetRunMessages": true
1818
},
19-
"CLI": {
19+
"Help": {
2020
"commandName": "Project",
2121
"commandLineArgs": "",
2222
"environmentVariables": {
2323
"ASPNETCORE_ENVIRONMENT": "Development"
2424
},
2525
"dotnetRunMessages": true
26+
},
27+
"Version": {
28+
"commandName": "Project",
29+
"commandLineArgs": "version",
30+
"environmentVariables": {
31+
"ASPNETCORE_ENVIRONMENT": "Development"
32+
},
33+
"dotnetRunMessages": true
2634
}
2735
},
2836
"$schema": "http://json.schemastore.org/launchsettings.json"

src/Directory.Build.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,6 @@
4343
<PackagePath>\</PackagePath>
4444
</None>
4545
<InternalsVisibleTo Include="$(MSBuildProjectName).Tests"/>
46+
<InternalsVisibleTo Include="DynamicProxyGenAssembly2"/>
4647
</ItemGroup>
4748
</Project>

src/K8sOperator.NET.Generators/Install.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using k8s;
22
using k8s.Models;
3+
using K8sOperator.NET.Builder;
34
using K8sOperator.NET.Extensions;
45
using K8sOperator.NET.Generators.Builders;
56
using K8sOperator.NET.Metadata;
@@ -9,15 +10,16 @@ namespace K8sOperator.NET.Generators;
910
/// <summary>
1011
///
1112
/// </summary>
12-
/// <param name="app"></param>
13-
/// <param name="writer"></param>
14-
public class Install(IOperatorApplication app, StringWriter writer)
13+
[OperatorArgument("install", Description = "Generates the install manifests")]
14+
public class InstallCommand(IOperatorApplication app) : IOperatorCommand
1515
{
16+
private readonly StringWriter _output = new();
17+
1618
/// <summary>
1719
///
1820
/// </summary>
1921
/// <returns></returns>
20-
public async Task RunAsync()
22+
public async Task RunAsync(string[] args)
2123
{
2224
var watchers = app.DataSource.GetWatchers(app.ServiceProvider);
2325
var clusterrole = CreateClusterRole(app.DataSource.Metadata, watchers);
@@ -34,12 +36,14 @@ public async Task RunAsync()
3436
await Write(clusterrole);
3537
await Write(clusterrolebinding);
3638
await Write(deployment);
39+
40+
Console.WriteLine(_output.ToString());
3741
}
3842

3943
private async Task Write(IKubernetesObject obj)
4044
{
41-
await writer.WriteLineAsync(KubernetesYaml.Serialize(obj));
42-
await writer.WriteLineAsync("---");
45+
await _output.WriteLineAsync(KubernetesYaml.Serialize(obj));
46+
await _output.WriteLineAsync("---");
4347
}
4448

4549
private static V1CustomResourceDefinition CreateCustomResourceDefinition(IEventWatcher item)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using K8sOperator.NET.Builder;
2+
3+
namespace K8sOperator.NET.Generators;
4+
5+
/// <summary>
6+
///
7+
/// </summary>
8+
public static class OperatorApplicationExtensions
9+
{
10+
/// <summary>
11+
///
12+
/// </summary>
13+
/// <typeparam name="TBuilder"></typeparam>
14+
/// <param name="builder"></param>
15+
/// <param name="order"></param>
16+
/// <returns></returns>
17+
public static IOperatorCommandConventionBuilder AddInstall<TBuilder>(this TBuilder builder, int? order = null)
18+
where TBuilder : IOperatorApplication
19+
{
20+
return builder.AddCommand<InstallCommand>(order ?? -1);
21+
}
22+
}
Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,13 @@
11
namespace K8sOperator.NET.Builder;
22

33
/// <summary>
4-
/// Desribes a Operator Convention Builder
4+
///
55
/// </summary>
6-
public interface IControllerConventionBuilder
7-
{
8-
/// <summary>
9-
/// Add Convention
10-
/// </summary>
11-
/// <param name="convention"></param>
12-
void Add(Action<IControllerBuilder> convention);
13-
14-
/// <summary>
15-
/// Add Finally Convention
16-
/// </summary>
17-
/// <param name="convention"></param>
18-
void Finally(Action<IControllerBuilder> convention);
19-
}
20-
6+
public interface IControllerConventionBuilder : IConventionBuilder<IControllerBuilder> { }
217

228
internal class ControllerConventionBuilder(
239
ICollection<Action<IControllerBuilder>> conventions,
24-
ICollection<Action<IControllerBuilder>> finallyConventions) : IControllerConventionBuilder
10+
ICollection<Action<IControllerBuilder>> finallyConventions)
11+
: ConventionBuilder<IControllerBuilder>(conventions, finallyConventions), IControllerConventionBuilder
2512
{
26-
public void Add(Action<IControllerBuilder> convention)
27-
{
28-
conventions.Add(convention);
29-
}
30-
31-
public void Finally(Action<IControllerBuilder> convention)
32-
{
33-
finallyConventions.Add(convention);
34-
}
3513
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
namespace K8sOperator.NET.Builder;
2+
3+
/// <summary>
4+
/// Desribes a Operator Convention Builder
5+
/// </summary>
6+
public interface IConventionBuilder<out T>
7+
{
8+
/// <summary>
9+
/// Add Convention
10+
/// </summary>
11+
/// <param name="convention"></param>
12+
void Add(Action<T> convention);
13+
14+
/// <summary>
15+
/// Add Finally Convention
16+
/// </summary>
17+
/// <param name="convention"></param>
18+
void Finally(Action<T> convention);
19+
}
20+
21+
internal class ConventionBuilder<T>(
22+
ICollection<Action<T>> conventions,
23+
ICollection<Action<T>> finallyConventions) : IConventionBuilder<T>
24+
{
25+
public void Add(Action<T> convention)
26+
{
27+
conventions.Add(convention);
28+
}
29+
30+
public void Finally(Action<T> convention)
31+
{
32+
finallyConventions.Add(convention);
33+
}
34+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
using K8sOperator.NET.Metadata;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using System.Reflection;
4+
5+
namespace K8sOperator.NET.Builder;
6+
7+
/// <summary>
8+
///
9+
/// </summary>
10+
public interface IOperatorCommandBuilder
11+
{
12+
/// <summary>
13+
///
14+
/// </summary>
15+
IServiceProvider ServiceProvider { get; }
16+
/// <summary>
17+
///
18+
/// </summary>
19+
List<object> Metadata { get; }
20+
}
21+
22+
/// <summary>
23+
///
24+
/// </summary>
25+
public interface IOperatorCommand
26+
{
27+
/// <summary>
28+
///
29+
/// </summary>
30+
/// <returns></returns>
31+
Task RunAsync(string[] args);
32+
}
33+
34+
internal class OperatorCommandBuilder(IServiceProvider serviceProvider, Type commandType) : IOperatorCommandBuilder
35+
{
36+
public IServiceProvider ServiceProvider { get; } = serviceProvider;
37+
public Type CommandType { get; } = commandType;
38+
public List<object> Metadata { get; } = [];
39+
40+
public IOperatorCommand Build()
41+
{
42+
var meta = CommandType.GetCustomAttribute<OperatorArgumentAttribute>(false) ?? new(CommandType.Name.Replace("Command", string.Empty).ToLower());
43+
Metadata.Add(meta);
44+
45+
return (IOperatorCommand)ActivatorUtilities.CreateInstance(ServiceProvider, CommandType);
46+
}
47+
}
48+
49+
/// <summary>
50+
///
51+
/// </summary>
52+
public static class OperatorCommandsBuilderExtensions
53+
{
54+
/// <summary>
55+
///
56+
/// </summary>
57+
/// <typeparam name="T"></typeparam>
58+
/// <returns></returns>
59+
public static IOperatorCommandConventionBuilder AddCommand<T>(this IOperatorApplication app, int order = 1)
60+
where T : class, IOperatorCommand
61+
{
62+
return app.Commands.AddCommand(typeof(T), order);
63+
}
64+
65+
/// <summary>
66+
///
67+
/// </summary>
68+
/// <typeparam name="TBuilder"></typeparam>
69+
/// <param name="builder"></param>
70+
/// <param name="argument"></param>
71+
/// <param name="description"></param>
72+
/// <returns></returns>
73+
public static TBuilder WithArgument<TBuilder>(this TBuilder builder, string argument, string description)
74+
where TBuilder : IOperatorCommandConventionBuilder
75+
{
76+
FinallyReplace(builder, new OperatorArgumentAttribute(argument) { Description = description});
77+
return builder;
78+
}
79+
80+
/// <summary>
81+
///
82+
/// </summary>
83+
/// <typeparam name="TBuilder"></typeparam>
84+
/// <param name="builder"></param>
85+
/// <param name="item"></param>
86+
/// <returns></returns>
87+
public static TBuilder FinallyReplace<TBuilder>(this TBuilder builder, object item)
88+
where TBuilder : IOperatorCommandConventionBuilder
89+
{
90+
builder.Finally(b => {
91+
b.Metadata.RemoveAll(x => x.GetType() == item.GetType());
92+
b.Metadata.Add(item);
93+
});
94+
95+
return builder;
96+
}
97+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+

2+
namespace K8sOperator.NET.Builder;
3+
4+
/// <summary>
5+
///
6+
/// </summary>
7+
public interface IOperatorCommandConventionBuilder : IConventionBuilder<IOperatorCommandBuilder> { }
8+
9+
internal class OperatorCommandConventionBuilder(
10+
ICollection<Action<IOperatorCommandBuilder>> conventions,
11+
ICollection<Action<IOperatorCommandBuilder>> finallyConventions)
12+
: ConventionBuilder<IOperatorCommandBuilder>(conventions, finallyConventions), IOperatorCommandConventionBuilder
13+
{
14+
}

src/K8sOperator.NET/Builder/OperatorHostBuilder.cs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Microsoft.Extensions.Configuration;
44
using Microsoft.Extensions.DependencyInjection;
55
using Microsoft.Extensions.Logging;
6+
using System.Data.Common;
67
using System.Reflection;
78

89
namespace K8sOperator.NET.Builder;
@@ -32,6 +33,11 @@ public interface IOperatorApplicationBuilder
3233
/// </summary>
3334
IControllerDataSource DataSource { get; }
3435

36+
/// <summary>
37+
///
38+
/// </summary>
39+
IKubernetesBuilder Kubernetes { get; }
40+
3541
/// <summary>
3642
/// Gets the list of metadata associated with the application.
3743
/// </summary>
@@ -66,6 +72,7 @@ internal OperatorApplicationBuilder(string[] args)
6672
{
6773
_logging = new(_serviceCollection);
6874
_kubernetes = new(_serviceCollection);
75+
6976
_args = args;
7077

7178
ConfigureConfiguration();
@@ -86,13 +93,11 @@ private void ConfigureMetadata()
8693

8794
_metadata.AddRange([operatorName, dockerImage]);
8895
}
89-
9096
public IConfigurationManager Configuration => _configurationManager;
9197
public IServiceCollection Services => _serviceCollection;
9298
public ILoggingBuilder Logging => _logging;
9399
public IKubernetesBuilder Kubernetes => _kubernetes;
94100
public IControllerDataSource DataSource => _datasource;
95-
96101
public List<object> Metadata => _metadata;
97102

98103
public IControllerConventionBuilder AddController(Type controllerType)
@@ -103,17 +108,21 @@ public IControllerConventionBuilder AddController(Type controllerType)
103108

104109
public IOperatorApplication Build()
105110
{
111+
_serviceCollection.AddSingleton<IConfiguration>(_configurationManager);
112+
_serviceCollection.AddSingleton<IControllerDataSource>(_datasource);
113+
_serviceCollection.AddSingleton<IOperatorApplication>(x => new OperatorHostApplication(x, _args));
114+
106115
var serviceProvider = _serviceCollection.BuildServiceProvider();
107-
return new OperatorHostApplication(serviceProvider, _configurationManager, DataSource);
116+
return serviceProvider.GetRequiredService<IOperatorApplication>();
108117
}
109118

110119
private void ConfigureConfiguration()
111120
{
112121
_configurationManager
113122
.SetBasePath(Directory.GetCurrentDirectory())
114-
//.AddJsonFile("appsettings.json", true, true)
115-
//.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", true, true)
116-
//.AddEnvironmentVariables()
123+
.AddJsonFile("appsettings.json", true, true)
124+
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", true, true)
125+
.AddEnvironmentVariables()
117126
.AddCommandLine(_args);
118127
}
119128

0 commit comments

Comments
 (0)